Design Pattern - Singleton Pattern (设计模式 - 单例模式)

什么是单例模式?

我们只允许某个类存在唯一的一个实例 - 换句话说,对于某个类我们只new 一次

为什么使用单例?

比如线程池, Metrics Object, 或者Logging Object etc.  单例可以用来减少对资源的损耗,因为对于相同目的操作我们应该尽可能的重用而不是重建。

如何使用单例?

方法一

package Chap5SingletonPattern;

public class Singleton_1 {
	private static Singleton_1 instance;
	
	private Singleton_1() {}
	
	public static Singleton_1 getSingleton() {
		if (instance == null) {
			instance = new Singleton_1();
		}
		
		return instance;
	}
}

这是最简单的方法,但是他是线程不安全的。 当多个线程同时访问  getSingleton 这个方法的时候,Singleton_1 可能会创建多个对象,这就打破了singleton pattern 的原则。

方法二            

package Chap5SingletonPattern;

public class Singleton_2 {
	private static Singleton_2 instance;
	
	private Singleton_2() {}
	
	public static synchronized Singleton_2 getSingleton() {
		if (instance == null) {
			instance = new Singleton_2();
		}
		
		return instance;
	}
}

通过添加 synchronized 关键字,我们把getSingleton 这个方法给转换成 thread safe 模式,这解决了线程安全问题,但是 synchronization 是比较消耗资源的,不仅如此,我们这里的线程安全指的是 当多个线程同时并且第一次 访问 getSingleton ,当第二次访问的时候就不存在问题。所以有点 overkill - 性能比较见。

方法三

package Chap5SingletonPattern;

public class Singleton_3 {
	private static Singleton_3 instance = new Singleton_3();
	
	private Singleton_3() {}
	
	public static Singleton_3 getSingleton() {
		return instance;
	}
}

我们依赖JVM 来创建 singleton object, 我们知道static variable 是在 JVM load class 的时候才会创建的所以 这里保证了 线程的安全. 他的性能和方法一的基本相同.

值得注意的是,JVM load class 只是load 要被使用的class。 所以这种方法能造成一种情况就是 我们 创建了这个singleton object  但是没有任何一个 thread来的及使用它。这就造成了某种程度上的浪费。

方法四

package Chap5SingletonPattern;

public class Singleton_4 {
	private volatile static Singleton_4 instance;
	
	private Singleton_4() {}
	
	public static  Singleton_4 getSingleton() {
		if (instance == null) {
			synchronized(Singleton_4.class) {
				instance = new Singleton_4();
			}
		}
		
		return instance;
	}
}
这里我们同时使用了 volatile 和 synchronized 关键字 - Double-Chekced locking. 保证了我们就在第一次的时候进行锁操作,并且保证了 当我们 在 某个进程的cache上实例化 singleton instance的时候,这个instance被扩散到所有的线程里面。
Synchronized 关键字 的性能

通过运行以下代码发现,Singleton_1 (无syncronized 关键字)的运行时间大概在14ms;但是SIngleton_2(有关键字)的运行时间大概在38ms。差距hin大啊. 

扫描二维码关注公众号,回复: 871507 查看本文章
package Chap5SingletonPattern;

public class main {
	public static void main(String[] args) {
		long startTime = System.currentTimeMillis();
		
		int threadNo = 1000000;
		main m = new main();
		
		for (int i = 0; i < threadNo; i++) {
			runtest r = m.new runtest();
			r.run();
		}
		
		long stopTime = System.currentTimeMillis();
		System.out.println(stopTime - startTime);
	}
	
	
	public class runtest implements Runnable {
		public runtest() {
		}
		public void run() {
			Singleton_1.getSingleton(); //Singleton_1 无synchronized 关键字,Singleton_2 有。
		}
	}
}
有问题欢迎指出!



猜你喜欢

转载自blog.csdn.net/ytdxyhz/article/details/80045827