不会吧,不会吧,都2021年了还不会单例模式

案例场景:
1、数据库连接池不会反复创建
2、spring单例bean的生成
3、全局属性的一些保存

1.懒汉模式(线程不安全)

单例模式注意三点:
1 构造私有
2 全局变量私有
3 静态方法返回单例对象

    private static Singleton_01 instance;

    private Singleton_01() {}

    public static  Singleton_01 getInstance() {
        if (null == instance){
            instance = new Singleton_01();
            return instance;
        }
        return instance;
    }
复制代码

为什么说懒汉模式的单例模式线程不安全呐?假如多线程时,线程A进入到getInstance方法,刚要创建对象,线程B争夺到CPU的执行权,也进入该方法,这时会发现创建了两个对象。接下来模拟一下这种情况:

public class Singleton_01 {

    private static Singleton_01 instance;

    private Singleton_01() {}

    public static  Singleton_01 getInstance() throws InterruptedException {
        if (null == instance){
            Thread.sleep(3000);
            instance = new Singleton_01();
            return instance;
        }
        return instance;
    }

    public static void main(String[] args) {
        new Thread(() -> {
            Singleton_01 instance = null;
            try {
                instance = Singleton_01.getInstance();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程A" + Thread.currentThread().getName() + ":" + instance);
        }).start();

        new Thread(() -> {
            Singleton_01 instance = null;
            try {
                instance = Singleton_01.getInstance();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程B" + Thread.currentThread().getName() + ":" + instance);
        }).start();
    }

}
复制代码

这里让getInstance休眠3秒钟,然后通过lambda表达式简化创建线程A、B,执行多次之后你会发现得的一下的结果:
线程B Thread-1:org.itstack.demo.design.Singleton_01@5015b31e
线程A Thread-0:org.itstack.demo.design.Singleton_01@74f84cf
我们得到了两个对象,说明懒汉模式是线程不安全的。

2.懒汉模式加锁(线程安全)

    private static Singleton_01 instance;

    private Singleton_01() {}

    public static synchronized Singleton_01 getInstance() {
        if (null == instance){
            instance = new Singleton_01();
            return instance;
        }
        return instance;
    }
复制代码

在方法级别上加上synchronized关键字,锁住该方法,保证在多线程情况下只能有一个线程进入该方法。用上边代码测试,会发现只会创建一个对象,线程安全

3.双重校验锁(线程安全)

    private static Singleton_01 instance;

    private Singleton_01() {}

    public static Singleton_01 getInstance() {
        if (null == instance){
        synchronized (Singleton_01.class){
                if (null==instance){
                    instance = new Singleton_01();
                }
            }
        }
        return instance;
    }
复制代码

双重校验锁是在内部,避免方法级别上锁造成的资源消耗,它会判断是否已经存在实例,如果不存在,直接锁住,然后再次校验是否存在实例,不存在创建该实例对象。

4.CAS(乐观锁,线程安全)

与syncronized悲观锁不同,CAS表示乐观锁,比较然后再重新尝试设置值。
CAS主要包含三部分:内存地址、旧值、新值。再每一次设置值之前,都是将旧值和根据内存地址获取的值作比较,如果相同,设置新值,不同则再次重试。

    private static final AtomicReference<Singleton_01> INSTANCE = new AtomicReference<Singleton_01>();

    private static volatile Singleton_06 instance;

    private Singleton_01() {
    }

    public static final Singleton_01 getInstance() {
        for (; ; ) {
            Singleton_01 instance = INSTANCE.get();
            if (null != instance){
                return instance;
            } 
            INSTANCE.compareAndSet(null, new Singleton_01());
            return INSTANCE.get();
        }
    }

复制代码

这里加入volatile关键字表示防止程序执行循序重新排序而造成的线程不安全问题。 乐观锁虽然好,但是也有缺点,就是会一直尝试获取,容易造成死循环

5.饿汉式单例模式(线程安全)

    private static Singleton_01 instance = new Singleton_01();

    private Singleton_01() {}

    public static  Singleton_01 getInstance() {
        return instance;
    }
复制代码

饿汉式设计模式会在类加载的时候创建对象,保证线程安全

6.静态内部类(线程安全)

    private static Singleton_01 instance = new Singleton_01();

    private Singleton_01() {}

    private static class CreateSingleton{
        private static Singleton_01 instance = new Singleton_01();
    }
    
    public static  Singleton_01 getInstance() {
        return CreateSingleton.instance;
    }
复制代码

这种采用内部类的方式创建线程,也会随着类的加载而创建单例对象,线程安全。

7.枚举方式(线程安全)

public enum Singleton_01 {
  INSTANCE;
}
复制代码

这种方式是Effective Java作者推荐的,是单例最好的实现方式,只是不用能与继承。

扫描二维码关注公众号,回复: 12663527 查看本文章

另外本人整理了20年面试题大全,包含spring、并发、数据库、Redis、分布式、dubbo、JVM、微服务等方面总结,下图是部分截图,需要的话点这里点这里,暗号CSDN。

猜你喜欢

转载自blog.csdn.net/qq_46388795/article/details/113179764