【设计模式】单例模式

1、最经典也是最简单的单例模式:

public class Singleton {

    private Singleton(){}

    private static Singleton instance;

    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

2、上述的单例模式是线程安全的吗?

答案当然是否,下面我们来验证下:以下代码模拟两个线程同时进入if(instance==null)判断。

public class Singleton {

    private Singleton(){}

    private static Singleton instance;

    public static Singleton getInstance(){
        if (instance == null) {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new Singleton();
        }
        return instance;
    }
}
public class SingletonTest {

    @Test
    public void testSingleton() throws InterruptedException {
        new SingleThreadDemo().start();
        new SingleThreadDemo().start();
        Thread.sleep(10000);

    }
}

class SingleThreadDemo extends Thread{
    @Override
    public void run() {
        Singleton s = Singleton.getInstance();
        System.out.println(s);
    }
}

结果:(可见这两个对象是不同的)

3、线程安全的单例模式

3.1、使用synchronized关键字修饰getInstance方法

3.2、静态对象直接实例化

public class Singleton {

    private Singleton(){}

    private static Singleton instance = new Singleton();
    
    public static Singleton getInstance() {
        return instance;
    }
}

3.3、使用双重检查锁

public class Singleton {

    private Singleton(){}
    
    // volatile确保每个线程取到的instance都是最新的
    private volatile static Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) {
            // 当instance为空,才锁住Singleton类
            synchronized (Singleton.class) {
                // 获得当前Singleton监视器的线程再作一次instance为空的判断
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

这里要使用volatile修饰instance,原因是,instance = new Singleton()可能发生指令重排序:

即:1、分配内存 2、初始化对象 3、设置instance指向内存地址 这三个步骤2和3有可能颠倒顺序,导致当多线程判断if(instance==null)时,有可能其中一个线程拿到还没有初始化但是已经有内存地址的instance对象。

如果有volatile,则能够使instance初始化后刷新到系统内存,而其他线程判断本地内存(缓存)中的instance和当前内存中的instance是否一致,如果不是则使缓存失效,重新读取。

 

还有一个神奇的现象,在JDK1.2之前,如果没有其他类引用本单例对象,本单例对象就会被垃圾回收,但是在JDK1.2之后,这个bug修复了

 

3.4、使用懒加载(比较难理解和掌握)

https://blog.csdn.net/qq_23900685/article/details/86899474

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值