美文网首页
单例模式有哪些写法

单例模式有哪些写法

作者: 张某某xyz | 来源:发表于2021-08-28 12:28 被阅读0次

    20210828

    饿汉模式、懒汉模式

    建议使用饿汉,因为简单

    饿汉模式

    类加载时就创建实例,缺点是假如这个单例类一直不被用到,创建出来的对象就是个浪费

    class SingleHungry {
        private static final SingleHungry singleHungry = new SingleHungry();
    
        private SingleHungry() {
        }
    
        public static SingleHungry getInstance() {
            return singleHungry;
        }
    }
    

    测试代码:

    public class SingleHungryTest {
        public static void main(String[] args) {
            List<String> list = new ArrayList();
            for (int i = 0; i < 10000; i++) {
                new Thread(() -> {
                    String hashcode = SingleHungry.getInstance().hashCode() + "";
                    if (list.isEmpty()) list.add(hashcode);
                    if (!list.contains(hashcode)) System.out.println("非单例");
                }).start();
            }
        }
    }
    

    懒汉模式

    当第一次用到时才创建实例

    class SingleLazy {
        // volatile是防止singleLazy = new SingleLazy()时指令重排
        private volatile static SingleLazy singleLazy;
    
        private SingleLazy() {
            MyThreadUtil.sleepSeconds(5);//模拟创建对象时的延时
        }
    
        /**
         * 2次if判,所以叫DCL:double check lazy
         */
        public static SingleLazy getInstance() {
            if (singleLazy == null) {
                synchronized (SingleLazy.class) {
                    if (singleLazy == null) singleLazy = new SingleLazy();
                }
            }
            return singleLazy;
        }
    }
    

    测试代码:

    public class SingleLazyTest {
        public static void main(String[] args) {
            List<String> list = new ArrayList();
            for (int i = 0; i < 10000; i++) {
                new Thread(() -> {
                    String hashcode = SingleLazy.getInstance().hashCode() + "";
                    if (list.isEmpty()) list.add(hashcode);
                    if (!list.contains(hashcode)) System.out.println("非单例");
                }).start();
            }
        }
    }
    

    为什么要写2次if(DCL)

    直接锁整个方法,正确,但是效率低

       public synchronized static SingleLazy getInstance() {
            if (singleLazy == null) singleLazy = new SingleLazy();
            return singleLazy;
        }
    

    等同于:

       public static SingleLazy getInstance() {
            synchronized (SingleLazy.class) {
                if (singleLazy == null) singleLazy = new SingleLazy();
            }
            return singleLazy;
        }
    

    上面2种写法的问题在于,当实例已经创建后,并发的线程还是会阻塞,于是改进成:

     public static SingleLazy getInstance() {
            if (singleLazy == null) {
                synchronized (SingleLazy.class) {
                    if (singleLazy == null) singleLazy = new SingleLazy();
                }
            }
            return singleLazy;
        }
    

    为什么要用volatile修饰实例

    private volatile static SingleLazy singleLazy;
    

    创建实例的代码singleLazy = new SingleLazy()对于的字节码指令是3个:

    1.创建实例,分配内存
    2.初始化实例
    3.将singleLazy指向分配的内存
    

    指令重排可能导致字节码的执行顺序是132,当一个线程发现singleLazy!=null,就直接返回他,但是实例还没有初始化完成,这样在使用实例时报错(指令重排成132的概率很小很小很小)

    相关文章

      网友评论

          本文标题:单例模式有哪些写法

          本文链接:https://www.haomeiwen.com/subject/svdcbltx.html