暮年|20年架构师深入讲解java多线程与高并发:volatile与CAS,涨薪5K( 三 )

<100; i++){new Thread(()->System.out.println(Mgr03.getInstance().hashCode())).start();}}}}开始进一步的吹毛求疵synchronized一下加在方法上这个代码太长了 , 说不定里面还有其他的业务逻辑 , 对于加锁这个事情 , 代码能锁的少的就要尽量锁的少 。 那么通过进一步的吹毛求疵又有了新的写法如下代码:线程判断 , 先别加锁 , 判断是否为空 , 如果为空在加锁初始化 , 更细粒度的一个锁 , 这叫做锁细化 , 也是锁优化的一步 。 很不幸的是这个写法是不对的 , 我们分析一下 , 第一个线程判断它为空 , 还没有执行下面的过程第二个线程来了 , 也判断它为空 。 第一个线程对它进行了加锁 , synchronized完了之后呢把锁释放了 , 而第二个线程也是判断为空拿到这把锁也初始化了一遍 , 所以这种写法是有问题的 。
public class Mgr05{private static Mgr05 INSTANCE;private Mgr05(){}public static Mgr05 getInstance(){if(INSTANCE == null){//妄图通过减小同步代码块的方式提高效率 , 然后不可行synchronized (Mgr05.class){try{Thread.sleep(1);}catch(InterruptedException e){e.printStace();}INSTANCE = new Mgr05();}return INSTANCE;}public void m(){System.out.println("m");}public static void main(String[] args){for(int i=0; i<100; i++){new Thread(()->System.out.println(Mgr03.getInstance().hashCode())).start();}}}}所以就产生了 , 我们今天要讲的volatile这个问题 , 这个问题是这样来产生的 , 看下面代码 , 叫做双重检查锁或者叫双重检查的单例 , 在这种双重检查判断的情况下刚才上面的说的线程问题就不会再有了 ,
分析一下:第一个线程来了判断ok , 你确实是空值 , 然后进行下面的初始化过程 , 假设第一个线程把这个INSTANCE已经初始化了 , 第二个线程 , 第一个线程检查等于空的时候第二个线程检查也等于空 , 所以第二个线程在 if(INSTANCE == null) 这句话的时候停住了 , 暂停之后呢第一个线程已经把它初始化完了释放锁 , 第二个线程继续往下运行 , 往下运行的时候它会尝试拿这把锁 , 第一个线程已经释放了 , 它是可以拿到这把锁的 , 注意 , 拿到这把锁之后他还会进行一次检查 , 由于第一个线程已经把INSTANCE初始化了所以这个检查通过了 , 它不会在重新new一遍 。 因次 , 双重检查这个事儿是能够保证线程安全的 。
就这个程序无论你运行多少遍 , 就算你在高并发的情况下运行 , 拿一百台机器同时访问这一台机子上的getInstance() , 每个机器上跑个一万个线程 , 使劲儿跑 , ok , 这个程序运行的结果也会是正确的 。
好 , 那么会有同学会说要不要加volatile? 这是一道面试题:你听说过单例模式吗 , 单例模式里面有一种叫双重检查的你了解吗 , 这个单例要不要加volatile?答案是要加的 , 我们这个实验很难做出来让它出错的情况 , 所以以前很多人就不加这个volatile他也不会出问题 , 不加volatile问题就会出现在指令重排序上 ,
第一个线程 INSTANCE = new Mgr06()经过我们的编译器编译之后呢的指令呢是分成三步 1.给指令申请内存 2.给成员变量初始化 3.是把这块内存的内容赋值给INSTANCE 。 既然有这个值了你在另外一个线程里头上来先去检查 , 你会发现这个值已经有了 , 你根本就不会进入锁那部分的代码 。
加了volatile会怎么样呢 , 加了volatile指令重排序就不允许存在了 。 对这个对象上的指令重排序不允许存在 , 所以在这个时候一定是保证你初始化完了之后才会赋值给你这个变量 , ok 这是volatile的含义 。
/*** lazy loading* 也称懒汉式* 虽然达到了按需求初始化的目的 , 但却能带来线程不安全的问题* 可以通过synchronized解决 , 但也带来效率下降*/public class Mgr06{private static /*volatile*/ Mgr06 INSTANCE;private Mgr06(){}public static Mgr06 getInstance(){if(INSTANCE == null){//双重检查synchronized (Mgr06.class){try{Thread.sleep(1);}catch(InterruptedException e){e.printStace();}INSTANCE = new Mgr06();}return INSTANCE;}public void m(){System.out.println("m");}public static void main(String[] args){for(int i=0; i