• 设计模式——单例模式


    单例模式

    传统的写法

    /**
    * 懒汉模式
    */
    public class Singleton {
        private static Singleton instance = null;
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    /**
     * 饿汉模式
     */
    class SingletonWithHungry {
        private SingletonWithHungry() {
        }
    
        private SingletonWithHungry instance = new SingletonWithHungry();
    
        public SingletonWithHungry getInstance() {
            return instance;
        }
    }
    
    /**
    * 枚举单例
    */
    public enum Girlfriend{
    	GIRLFRIEND;
    }
    
    
    /**
    * 静态内部类
    */
    public class SingleTon{
      private SingleTon(){}
     
      private static class SingleTonHoler{
         private static SingleTon INSTANCE = new SingleTon();
     }
     
      public static SingleTon getInstance(){
        return SingleTonHoler.INSTANCE;
      }
    }
    
    

    懒汉模式的线程不安全复现

    class TestSingleWithThread {
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(() -> {
                System.err.println(Singleton.getInstance());
            });
            Thread t2 = new Thread(() -> {
                System.err.println(Singleton.getInstance());
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
        }
    }
    

    解决方案:

    1. 懒人同步——getInstance方法加上synchronized关键字
    2. DCL——Double Checked Lock双重检查锁定

    DCL:

    class SingletonThreadSafeDCL {
        private static SingletonThreadSafeDCL instance = null;
    
        private SingletonThreadSafeDCL() {
        }
    
        public static SingletonThreadSafeDCL getInstance() {
            if (instance == null) {//(1)
                synchronized (SingletonThreadSafeDCL.class) {//(2)
                    if (instance == null) {//(3)
                        instance = new SingletonThreadSafeDCL();//(4)
                    }
                }
            }
            return instance;
        }
    }
    

    关于DCL存在的问题,参考《并发编程的艺术》3.8节。

    线程执行到(1)时,代码读取到的instance不为null,instance引用的对象可能还没完成初始化

    而new SingletonThreadSafeDCL()在JVM中分为三步:

      1.在堆内存开辟内存空间。
      2.在堆内存中实例化SingleTon里面的各个参数。
      3.把对象指向堆内存空间。
    

    由于重排序,可能导致3在2之前执行,

    因此,导致线程B访问到了一个未初始化的对象。

    比如单例有属性name,在新建实例时name 初始化为Michael,线程B访问到instance时,name未完成初始化,两个线程读到的值不一致。

    解决DCL

    1. volatile,禁止重排序

    扩展: volatile happens-before, volatile JMM语义

    1. 基于类初始化的解决方案
    当你准备好了,机会来临的时候,你才能抓住
  • 相关阅读:
    spring的工厂类
    spring的基于XML方式的属性注入
    github上传大于100M的文件报错
    fatal: HttpRequestException encountered
    VAR 学习笔记3
    You are my great sunshine
    warning: LF will be replaced by CRLF in
    术语词汇
    XGBoost学习笔记2
    四大基本算法思想
  • 原文地址:https://www.cnblogs.com/studentytj/p/11541454.html
Copyright © 2020-2023  润新知