public class demo02 { private static volatile demo02 INSTANCE; private demo02(){} public static demo02 getINSTANCE(){ if (INSTANCE == null){ synchronized (demo02.class){ if (INSTANCE == null){ try { Thread.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); } } INSTANCE = new demo02(); } } return INSTANCE; } public void d(){ System.out.println("d"); } public static void main(String[] args){ for (int i = 0 ; i < 100; i++){ new Thread(() ->{ System.out.println(demo02.getINSTANCE().hashCode()); }).start(); } } }
1、 暂不初始化对象,直到有其他对象或方法来调用时再进行初始化,避免造成不必要的初始化,减少内存空间; 2、 多线程访问时,加锁 3、 在其他对象使用该类,调用getInstance()方法的时候进行初始化,并且进行double check null ,double check null 的目的是在多线程的时候会造成多次new,而导致不是同一个对象也不是同一内存地址; 3、 volatile关键字的作用是,原子性(某些情况下不具备原子性),以及多线程之间的可见性,并且还有一点重要的是可以防止指令重排; 指令重排: 在一个class 通过class loader调用后进入内存中,到达linking阶段,linking阶段包括verification(验证) -> preparation(准备) -> resolution(解析); preparation阶段为clss->静态变量赋默认值(类的初始化),而在这一阶段可能会发生指令重排,(在其汇编代码内指令执行的顺序会发生变化)