一、懒汉式,线程不安全

懒汉式,线程不安全
所为懒汉式,就是在需要使用这个类的对象实例的时候才会创建。
上图是线程非安全的,如果多线程去调用 getInstances() 的时候会创建多个实例,也就是说多线程情况下是不安全的。
二、懒汉式,线程安全
针对上面的线程不安全,推出了懒汉式线程安全的一种写法。在getInstances()前加上synchronized

懒汉式,线程安全。
如上图,解决了线程安全及多实例的问题,但它的效率并不高,任何时候只有一个线程去调用getInstances()
针对上面的问题,引入双重检验锁
三、双重检验锁

双重检验锁
这里进行了两次 null == instances ,第一次判断是为了避免不必要的同步,第二次判断是确保在此之前没有其他线程进入到sychronized块创建了新实例(简单讲就是防止创建多个实例)。
这种写法也会出现问题,也不是最优解。主要在于instances = new Study()这句,这并非是一个原子操作。
因为instances = new Study() 转换成指令之后主要做一下几件事:
(1)给Study 实例分配内存
(2)调用Study 构造函数来初始化成员变量。
(3)讲instances 对象指向指定的内存。
上面三个步骤中(2)和(3)并不一定是顺序执行的,可能会先执行(3)再执行(2),因为JVM的即时编辑器中存在指令重排。
优化方案:

添加volatile
使用 volatile 的主要原因是其另一个特性:禁止指令重排序优化。也就是说,在 volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。
四、饿汉式
饿汉式,比较形象,饿了就比较急,所以在装载类的时候就创建对象实例。

饿汉式