Java多线程 -- "volatile禁止指令重排序"详解

单例模式: 指的是 " 一个类有且只有一个实例 "

volatile关键字: 保证"有序性, 可见性" 

synchronized关键字: 保证"原子性, 可见性"

Java多线程 --

规范版本:

Java多线程 --

问题1: Java单例模式双重校验锁的第一次是否为空的判断是什么目的?

   已知: 在使用多线程时, 需要在多个线程访问共享资源之前加锁(即 synchronized块会进行一次是否为空的判断), 然后再进行操作. 因此, 一个类只创建一个实例这点得到了保障.

   但是如果只在synchronized块里进行一次对象是否为空的判断, 会使得synchronized块总是会被执行到, 意味着每个线程执行getInstance方法时都必须获得一个内部对象锁, 于是就大大增加了锁的获得以及锁的释放的开销. 而如果在执行synchronized块之前先进行一次对象是否为空的判断, 那么就降低了synchronized块被多个线程执行到的几率(即避免了锁竞争), 因此降低了锁的开销,提高了性能

       

synchronized在第二次判断条件那里加锁, 原因是会引起 " 锁竞争 " , 锁竞争现象会影响效率, 有了锁竞争, 线程都会 " 同步互斥 ", 因此为了提高效率, 将锁放到第一层判断之外.

       进行第一次变量是否为空的判断时, 要满足多线程下数据的读取操作, 会涉及到读取操作的安全性, 而将第一次判断放在锁之外, 只是获取到了变量的值, 然后进行判断了一下, 因此只涉及到了变量的读取操作, 涉及到了变量的可见性问题, 因此会使用volatile关键字来保证它的可见性

   volatile的可见性: 一个线程在进行写操作的同时, 可以被其他正在进行读操作的线程立即看到.

问题2: 为什么synchronized要在第二次判断条件那里加锁?

        因为在锁竞争之后, 为了保证单例模式是一个对象(单例,即就是指只有一个实例对象),  要加上变量是否为空的判断条件, 如果变量等于空, 才进行new操作创建一个对象, 否则不进行新对象的创建的. 如果不加上这个判断条件, 每次都是创建了一个新对象, 不满足单例模式的设计了

问题3: 对volatile禁止指令重排序的作用的理解.

        多线程下的执行逻辑: 假如有10个线程在运行, 10个线程都可以在同一时间点调用getDataSource()这个方法.

这里要理解的前提知识有:

1. 在同一时间点, 可能有多个线程要执行某一代码行或者某一代码块.

2. synchronized对对象进行加锁操作时, 会造成线程执行代码同步互斥

   没有加锁之前(即第一个判断对象是否为空的if语句), 所有线程都可以同时执行的, 加了锁之后(第二个if语句), 只有获取这个对象锁的线程才可以执行, 其他线程阻塞等待, 因此这段代码(第二个if判断语句里的代码)就只能在获取到对象锁的那个线程里执行

给某个线程加上对象锁, 而其他线程尝试获取同一个对象锁, 在执行到synchronized这个代码行,并尝试获取锁的时候, 因为获取不到,就阻塞等待(注意, 这里一定要强调是获取同一个对象锁, 因为如果线程不是获取同一个对象锁, 也是可以同时执行的)

n++操作和new操作是非原子性的, 就比如下图代码中的new操作, 会分为三个指令:(虽然在java源文件里看起来是一行代码,但java文件最终会编译成class文件,class文件在虚拟机/CPU里会分解为更细的指令)

Java多线程 --

Java多线程 --

Java多线程 --

 

 

 

 

 

你可能感兴趣的:(Java多线程)