懒汉模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class SingletonDemo {
private static volatile SingletonDemo instance; private String value;
public static SingletonDemo getInstance() { if (instance == null) { instance = createInstance(); } return instance; }
private static SingletonDemo createInstance() { return new SingletonDemo(); }
private SingletonDemo() { } }
|
- 使用private的构造函数,确保无法通过new的方式,来创建一个新的实例,在使用new的时候会提示。

- 通过volatile保证,在获取的时候,实例始终是最新的。
- 在使用getInstance获取实例的时候,如果是第一次获取,会调用private的创建实例的方法,来进行实例的创建,然后赋值给static的变量。
- 上面会存在一个问题,可能同时有多个线程进行实例的创建,例如10个线程同时执行获取实例方法,并且instance都为空,那么每个线程都会尝试去调用createInstance的方法来进行instance的创建,就会产生无效的性能损耗
为了验证这个情况,我们可以通过测试来验证下
1 2 3 4 5
| private static SingletonDemo createInstance() { System.out.println("开始创建"); return new SingletonDemo(); }
|
书写测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| CyclicBarrier cyclicBarrier = new CyclicBarrier(10); for (int i = 0; i < 10; i++) { new Thread(() -> { try { cyclicBarrier.await(); SingletonDemo singletonDemo = SingletonDemo.getInstance(); } catch (Exception e) { System.out.println("出现了异常"); } }).start(); }
|
保证10个线程,同时开始进行实例的获取,然后看输出情况

通过结果我们能发现,createInstance被调用了十次,而这十次里面有九次都是我们不希望出现的。
双重检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class SingletonDemo { private static volatile SingletonDemo instance; public static SingletonDemo getInstance() { if (instance == null) { synchronized (SingletonDemo.class) { if (instance == null) { instance = createInstance(); } } } return instance; } private static SingletonDemo createInstance() { System.out.println("开始创建"); return new SingletonDemo(); } private SingletonDemo() { } }
|
通过第一种方法的测试,我们能够发现,同时有多个线程进入到了createInstance的阶段,准备创建实例。现在我们需要对SingletonDemo类进行同步,确保同时只有一个线程能够拿到SingletonDemo的锁,然后进入到下面的代码。
为了保证在拿到锁的时候,实例还没有被创建出来,我们需要进行二次检查,避免一个线程创建完成之后,另外一个线程拿到锁,再次进入然后创建对象。
如果在检测到实例为null之后,会先尝试获取锁,如果拿到锁,并且实例还是为空,因为实例使用了volatile关键字,保证了时效性,所以这个时候一定是没有别的线程抢到锁并且开始创建对象,持有锁的线程可以放心的创建对象,然后返回。之后的线程如果拿到了锁,但是由于实例已经被创建出来了,因此也不会进入到创建实例的方法。
我们再次测试验证下,发现的确只打印了一次。

静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class SingletonInnerDemo { private SingletonInnerDemo() { System.out.println("开始创建"); } public static SingletonInnerDemo getInstance() { return inner.instance; } static class inner { private static final SingletonInnerDemo instance = new SingletonInnerDemo(); } }
|
- 通过使用内部类的方式,来限定访问的范围。设定了inner内的instance的访问范围为private,也就是无法在该类的外部进行访问。保证了无法通过直接调用的方式来进行新对象的创建。
- 使用私有化构造函数,保证无法通过new的方式来进行创建
- 因为是内部类,所以只有在第一次被引用的时候,才会进行初始化
- 因为在初始化的时候,使用的是类加载进行实例的加载,能够保证线程的安全性。