饿汉式单例
浪费资源
/**
* 饿汉式单例
* @author li
*/
public class Hungry {
private Hungry() {
}
public final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式单例、双重检验锁懒汉式单例
双重检验锁懒汉式单例也不安全,可以通过反射破坏
/**
* 懒汉式单例
* @author li
*/
public class LazMan {
private LazMan(){
System.out.println(Thread.currentThread().getName()+"ok");
}
/**
* ⑥加上volatile 避免指令重排 这才是一个完整地双重检验锁的懒汉式单例
* ps:volatile不能保证原子性操作
*/
private volatile static LazMan lazMan;
/**
* ①
* 这种方式单线程下是可以的
* @return
* ④加完锁之后的单例就是双重检验锁的懒汉式单例 也就是DCL懒汉式 但是还不安全!!!
*/
public static LazMan getInstance() {
if (lazMan == null){
// ③加锁 synchronized 保证这个类只有一个
synchronized (LazMan.class){
if (lazMan==null){
/**
* ⑤ new LazMan();不是一个原子性操作
* 实例化对象会经过三个步骤
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 有两种顺序 123 132
* 当线程A以132的方式实例化对象时(先占用空间,在初始化对象,也是可以的),
* 线程B进来了(那么lazMan就不是空了),
* 此时lazMan还没有完成构造那么就会出问题
*/
lazMan = new LazMan();
}
}
}
return lazMan;
}
/**
* ②
* 演示上面的单例在多线程下出现的问题
* Thread-1ok
* Thread-3ok
* Thread-0ok
* Thread-2ok
*/
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazMan.getInstance();
}).start();
}
}
}