单例模式
在编程中,单例模式是我们常用的一种设计模式,功能是保证在整个系统只用一个该对象的对象,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); return singleton; } return singleton; } }
|
上面的代码我们知道并不是线程安全的,在多线程环境下,容易造成创建多个对象。 测试代码如下:
1 2 3 4 5 6 7 8 9 10 11
| @Test public void testSingleton() throws InterruptedException { for (int i=0;i<10;i++){ new Thread(()->{ Singleton.getInstance(); }).start(); } Thread.currentThread().join(); }
|
运行结果如下:
1 2 3 4 5 6 7
| 创建对象 创建对象 创建对象 创建对象 创建对象 创建对象 创建对象
|
解决方案
对于上面的问题解决的方法有很多,比如使用加锁的方式,double检测的方式,为了验证最有方案我们把代码修改下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Singleton { private static Singleton singleton; private Singleton() { try { Thread.sleep(10); } catch (Exception e) { } } public static Singleton getInstance() { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); return singleton; } } return singleton; } }
|
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Test public void testSingleton() throws InterruptedException { long start=System.currentTimeMillis(); List<Thread> threadList = new ArrayList<>(); for (int i = 0; i < 1000; i++) { Thread thread = new Thread(() -> { Singleton.getInstance(); }); threadList.add(thread); thread.start(); } for (Thread t : threadList) { t.join(); } long end=System.currentTimeMillis(); System.out.println("运行耗时:"+(end-start)); }
|
方案一:使用synchronized 关键字
1 2 3 4 5 6 7 8 9 10
| public static Singleton getInstance() { synchronized (Singleton.class){ if (singleton == null) { System.out.println("创建对象"); singleton = new Singleton(); } } return singleton; }
|
经过多次测试时间维持在410ms左右,下面是一次测试结果
这个虽然成功的保证了只有一个对象,但同样也会把其他的线程阻塞在创建的锁的前面,造成了性能上面的开销,如果创建一个对象的时间比较长,这个性能的开销是相当可观的。
方案二:double验证
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); return singleton; } } } return singleton; }
|
上面的代码虽然聪明的避开的过多线程等待的原因,但是彻底消除线程排队的现象,因为创建对象分需要耗时,这样就给其他线程提供了“可乘之机”
方案三:使用volatile共享变量 (最优方案)
共享变量是线程间同步的“轻量级锁”,彻底消除线程排队的现象,此处用于单例模式的设计,能够实现最小性能的开销:
1 2
| private volatile static Singleton singleton;
|