设计模式之懒汉模式线程问题

懒汉模式的导致的线程问题

  • 普通懒汉模式代码:
public class Singleton {

    private static Singleton singleton = null;

    /**
     * 私有化实例,防止new出对象
     */
    private Singleton(){}

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

}

问题:由于多线程的情况下线程执行是非常快的,如果是饿汉模式下确实保证了一个实体类只存在一个对象,但是在懒汉模式下由于需要对当前对象进行判断是否为空,在多条线程执行的情况下如果第一条线程正在执行如下代码:

 if (singleton == null)

但是此时还没有执行singleton = new Singleton();的时候,第二条线程进入,这时两条或多条线程同时进行:

singleton = new Singleton();

那么这两条线程获取到的并不是同一个对象 

  • 有三种解决方式:
  1. 双重判断,这里最大的疑问可能就是第一重判断是否需要,仔细去想就会发现第一重判断会筛选掉大部分的线程获取锁,只会有小部分的线程执行到线程锁的那一步,我们都知道synchronized 的效率是比较低的,如果没有第一重判断每个线程都去获取锁会大大降低执行的效率,减少死锁等风险
    public class SingletonThread {
    
        private static volatile SingletonThread singleton = null;
    
        /**
         * 私有化实例,防止new出对象
         */
        private SingletonThread(){}
    
        public static SingletonThread getInstance() {
            if (singleton == null) {
                synchronized (SingletonThread.class) {
                    if (singleton == null) {
                        singleton = new SingletonThread();
                    }
                }
            }
            return singleton;
        }
    
    }

     

  2. 静态内部类方式:静态内部类在外部类被加载的时候内部类是不会被加载的,只有调用getInstance方法后这个类才会被加载
    public class SingletonThread {
    
    
        private static SingletonThread singleton = null;
    
        /**
         * 私有化实例,防止new出对象
         */
        private SingletonThread(){}
    
        private static class Holder {
            private final static SingletonThread singletonThread = new SingletonThread();
        }
    
        public static SingletonThread getInstance() {
            return Holder.singletonThread;
        }
    
    }
    

     

  3. 通过枚举创建实例,因为枚举是默认为静态的,并且默认为私有的,每一个枚举系统会自动添加 public static final 修饰,所以无论如何创建枚举不存在线程安全问题,并且永远是单例:
    /**
     * 不仅可以解决线程问题,还可以防止反射导致的多实例问题
     * 枚举的特性:
     *      <p>1.枚举类是一种特殊的类,它和普通的类一样,有自己的成员变量、成员方法、构造器 (只能使用 private 访问修饰符,所以无法从外部调用构造器,构造器只在构造枚举值时被调用);
     *      例如:SingletonThread2 instance = SingletonThread2.INSTANCE 此时枚举被调用</p>
     *      <p>2.使用 enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口;</p>
     *      <p>3.所有的枚举值都是 public static final 的,且非抽象的枚举类不能再派生子类;</p>
     *      <p>4.枚举类的所有实例(枚举值)必须在枚举类的第一行显式地列出,否则这个枚举类将永远不能产生实例。列出这些实例(枚举值)时,系统会自动添加 public static final 修饰,无需程序员显式添加。</p>
     *
     */
    public enum  SingletonThread2 {
        INSTANCE;
    }

     

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值