👍 👎 💯 👏 🔔 🎁 ❓ 💣 ❤️ ☕️ 🌀 🙇 💋 🙏 💦 💩 ❗️ 💢
————————————————
文章目录
知识点
什么是单例模式
- 单例模式是一种常见的设计模式,所谓设计模式就是在开发过程中为了避免出现很多问题所规定的一套"'棋谱"一样的东西
- 常见场景:在数据库JDBC中,我们用到的DataSource这样的类就是一个单例模式的类,在一个程序中,就只有一个实例,不应该创建多个DataSource对象,还有一些服务器里用到的数据管理器,也是单例的
- 使用两种经典的方式 饿汉模式 和 懒汉模式 就可以实现以上场景,保证一个类只能有一个实例
饿汉模式
- 饿汉模式的类在类被加载的过程中,就会立刻实例化一个对象,所以后续无论如何操作,只要严格使用get方法,就不会出现其他的实例
- 由上可知,饿汉模式是线程安全的,但是他也会哟一个问题就是我不适用这个类,他还会创建一个实例占用我们的内存,这就导致他的效率不高
//饿汉模式 饿汉模式就是在类加载的时候就将唯一的实例创建好,线程安全,但是效率低
class HungrySingle{
private HungrySingle(){}
private static HungrySingle single = new HungrySingle();
public static HungrySingle getSingle(){
return single;
}
}
public class ThreadTest {
public static void main(String[] args) {
HungrySingle single = HungrySingle.getSingle();
HungrySingle single1 = HungrySingle.getSingle();
System.out.println(single == single1);
}
}
饿汉模式
- 懒汉模式就改进了饿汉模式的缺点,他只有在使用的时候才会让该类去实例化一个对象,并且此后再去获取对象,只能获取这一个对象
- 所以我们一般认为懒汉模式比饿汉模式的效率更高,但是懒汉模式也有缺点:他的线程不安全
//懒汉模式 懒汉模式是在用到该实例的时候采取创建一个实例,后面如果再创建实例只是返回原有实例,线程不安全
class LazySingle {
private LazySingle() {
}
private static LazySingle single;
public static LazySingle getSingle() {
if (single == null) {
single = new LazySingle();
}
return single;
}
}
public class ThreadTest {
public static void main(String[] args) {
LazySingle single = LazySingle.getSingle();
LazySingle single1 = LazySingle.getSingle();
System.out.println(single == single1);
}
}
高性能版懒汉模式
- 当我们考虑多线程的时候,我们就会发现懒汉模式的单例模式在多线程中是会出现多个线程修改同一个变量,那么他必然会导致线程不安全,如何解决这点,实现原子操作即可
- 而实现原子操作就是为其上锁
//懒汉模式 懒汉模式是在用到该实例的时候采取创建一个实例,后面如果再创建实例只是返回原有实例,线程不安全
class LazySingle {
private LazySingle() {
}
private static LazySingle single;
public synchronized static LazySingle getSingle() {
if (single == null) {
single = new LazySingle();
}
return single;
}
}
public class ThreadTest {
public static void main(String[] args) {
LazySingle single = LazySingle.getSingle();
LazySingle single1 = LazySingle.getSingle();
System.out.println(single == single1);
}
}
- 但是我们发现上面的写法将return语句也包含在原子操作里面,这样虽然可以解决线程安全问题,但是锁的粒度太大,将一些原本无关紧要的代码或者说本来可以并发执行的代码也变成了原子操作,这样导致的粒度大就会降低这段代码的并发执行能力,降低了效率
- 所以我们再次优化
//懒汉模式 懒汉模式是在用到该实例的时候采取创建一个实例,后面如果再创建实例只是返回原有实例,线程不安全
class LazySingle {
private LazySingle() {
}
private static LazySingle single;
public static LazySingle getSingle() {
synchronized (LazySingle.class) {
if (single == null) {
single = new LazySingle();
}
}
return single;
}
}
public class ThreadTest {
public static void main(String[] args) {
LazySingle single = LazySingle.getSingle();
LazySingle single1 = LazySingle.getSingle();
System.out.println(single == single1);
}
}
- 此时我们继续观察就会发现,还有一个特别重要的问题需要优化,如果有很多线程,后面一些线程再去获取锁释放锁这样的操作就会降低他们的效率,所以在加锁外面再判断一次是否为空,这样就可以保证后续线程的效率
//懒汉模式 懒汉模式是在用到该实例的时候采取创建一个实例,后面如果再创建实例只是返回原有实例,线程不安全
class LazySingle {
private LazySingle() {
}
private static LazySingle single;
public static LazySingle getSingle() {
if(single == null){
synchronized (LazySingle.class) {
if (single == null) {
single = new LazySingle();
}
}
}
return single;
}
}
public class ThreadTest {
public static void main(String[] args) {
LazySingle single = LazySingle.getSingle();
LazySingle single1 = LazySingle.getSingle();
System.out.println(single == single1);
}
}
- 最终版本
- 当我们在画时间线的时候就会发现,线程2尝试获取锁的时候,可能编译器会对其进行优化,这样一旦被优化就会出现内存可见性的安全问题,所以我们需要给single对象加上一个volatile去保证他的内存可见性
//懒汉模式 懒汉模式是在用到该实例的时候采取创建一个实例,后面如果再创建实例只是返回原有实例,线程不安全
class LazySingle {
private LazySingle() {
}
private volatile static LazySingle single = null;
public static LazySingle getSingle() {
if(single == null){
synchronized (LazySingle.class) {
if (single == null) {
single = new LazySingle();
}
}
}
return single;
}
}
public class ThreadTest {
public static void main(String[] args) {
LazySingle single = LazySingle.getSingle();
LazySingle single1 = LazySingle.getSingle();
System.out.println(single == single1);
}
先赞后看,养成习惯!!!^ _ ^♥♥♥
每天都更新知识点哦!!!
码字不易,大家的支持就是我坚持下去的动力。点赞后不要忘记关注我哦!