多线程中单例模式的优化

老付     2018-12-02   2169   6min  

单例模式

在编程中,单例模式是我们常用的一种设计模式,功能是保证在整个系统只用一个该对象的对象,具体代码如下:


public class Singleton {
	private static Singleton singleton;
	
	private Singleton() {
	}
	
	public static Singleton getInstance() {
		if (singleton == null) {
			singleton = new Singleton();
			return singleton;
		}
		return singleton;
	}
}

上面的代码我们知道并不是线程安全的,在多线程环境下,容易造成创建多个对象。 测试代码如下:


	@Test
	public void testSingleton() throws InterruptedException {
		for (int i=0;i<10;i++){
			new Thread(()->{
				Singleton.getInstance();
			}).start();
		}
		Thread.currentThread().join();
	}

运行结果如下:

创建对象
创建对象
创建对象
创建对象
创建对象
创建对象
创建对象

解决方案


对于上面的问题解决的方法有很多,比如使用加锁的方式,double检测的方式,为了验证最有方案我们把代码修改下:


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;
	}
}

测试代码


@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 关键字

public static Singleton getInstance() {
		synchronized (Singleton.class){
			if (singleton == null) {
				System.out.println("创建对象");
				singleton = new Singleton();
			}
		}
		return singleton;
	}

经过多次测试时间维持在410ms左右,下面是一次测试结果


运行耗时:410

这个虽然成功的保证了只有一个对象,但同样也会把其他的线程阻塞在创建的锁的前面,造成了性能上面的开销,如果创建一个对象的时间比较长,这个性能的开销是相当可观的。

方案二:double验证


	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) {
				if (singleton == null) {
					singleton = new Singleton();
					return singleton;
				}
			}
		}
		return singleton;
	}


运行耗时:380

上面的代码虽然聪明的避开的过多线程等待的原因,但是彻底消除线程排队的现象,因为创建对象分需要耗时,这样就给其他线程提供了“可乘之机”

方案三:使用volatile共享变量 (最优方案)

共享变量是线程间同步的“轻量级锁”,彻底消除线程排队的现象,此处用于单例模式的设计,能够实现最小性能的开销:

private volatile static Singleton singleton;


运行耗时:280

(本文完)

作者:付威

如果觉得对您有帮助,可以下方的RSS订阅,谢谢合作

如有任何知识产权、版权问题或理论错误,还请指正。

本文是付威的网络博客原创,自由转载-非商用-非衍生-保持署名,请遵循:创意共享3.0许可证

交流请加群113249828: 点击加群   或发我邮件 laofu_online@163.com