单例模式

饿汉式

public class hungry {  
    private hungry() {  
        System.out.println("I am hungry");  
    }  
    private static hungry instance = new hungry();  
    public static hungry getInstance() {  
        return instance;  
    }  
    public static void main(String[] args) {  
        hungry h1 = hungry.getInstance();  
        hungry h2 = hungry.getInstance();;  
  
        System.out.println(h2);  
        System.out.println(h1);  
    }  
}

浪费内存空间

懒汉式

public class LazyMan {  
    private LazyMan() {  
        System.out.println("I am LazyMan");  
    }  
  
    private static LazyMan instance;  
  
    public static LazyMan getInstance() {  
        if (instance == null) {  
            instance = new LazyMan();  
        }  
        return instance;  
    }  
}

但是多线程下这个写法会出现问题。

public class LazyMan {  
    private LazyMan() {  
        System.out.println("I am LazyMan");  
    }  
  
    private static LazyMan instance;  
  
    public static LazyMan getInstance() {  
        if (instance == null) {  
            instance = new LazyMan();  
        }  
        return instance;  
    }  
    public static void main(String[] args) {  
        for (int i = 0; i < 10; i++) {  
            new Thread(() -> {  
                getInstance();  
            }).start();  
        }  
    }  
}

image.png

可以看到实例被创建了多次,解决的方法很简单,加锁!

package single;  
  
public class LazyMan {  
    private LazyMan() {  
        System.out.println("I am LazyMan");  
    }  
  
    private static LazyMan instance;  

	//双重判断
    public static LazyMan getInstance() {  
        if (instance == null) {  
            synchronized (LazyMan.class) {  
                if (instance == null) {  
                    instance = new LazyMan();  
                }  
            }  
        }  
        return instance;  
    }  
    public static void main(String[] args) {  
        for (int i = 0; i < 10; i++) {  
            new Thread(() -> {  
                getInstance();  
            }).start();  
        }  
    }  
}

但现在还有问题: new LazyMan不是一个原子性操作,分为三步:

  1. 分配内存空间
  2. 执行构造方法,实例化对象
  3. 把对象指向内存空间 那么就有可能存在一个指令重排现象,比如执行顺序变为1,3,2。那么会发生什么呢?