单例的三种可用写法

单例的三种可用写法

      单例是通过代码的写法达到一个实例在一个堆中永远只有一个的目的,从而去除内存不必要的开销或达到某些实例(例如数据库连接池、常见框架中的控制-事物-持久层等)永远唯一的目的。笔者总结了以下三种可用的单例写法,懒汉/饿汉式的写法不再赘述

双重判定锁
/**
 * 双重判定锁
 */
public class Singleton {
	private static volatile Singleton singleton;
	private Singleton() {
		super();
	}
	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized (Singleton.class) {
				if (singleton == null)
					singleton = new Singleton();
			}
		}
		return singleton;
	}
	
	public static void main(String[] args) {
		for (int i = 0; i < 1000; i++) {
			Runnable run = () -> System.out.println(Singleton.getInstance());
			run.run();
		}
	}
}
  • 这种写法是最安全的方案,注意Singleton前的volatile必须加上,一个对象的创建分为两步:实例化对象和开辟内存。这两步很可能会通过CPU的重排序执行机制发生先后顺序变更(因为创建对象的过程已经被上锁,是通过同一个线程执行的,且上下文没有数据关联),也就意味着可能先开辟内存再实例化对象,在A线程开辟内存但实例化对象前,B线程去获取singleton是可以获取到的,虽然singleton中没有任何数据,那么B线程在使用获取到的对象时就会抛异常了。当然,这种问题出现的几率非常小。
静态属性
/**
 * 作为静态常量属性的单例
 */
class Singleton2 {
	public static final Singleton2 singleton2 = new Singleton2();
	private Singleton2() {
		super();
	}
	
	public static void main(String[] args) {
		for (int i = 0; i < 100; i++) {
			Runnable run = () -> System.out.println(Singleton2.singleton2);
			run.run();
		}
	}
}
  • 这种写法是最简便的方案,利用静态域的特性,一个单例可以毫不费力地创建,但这种写法将单例作为静态属性且在属性加载时直接将其实例化的做法会产生无用的内存开销,当然,如果实例不大的话也无关紧要了
静态内部类
/**
 * 作为静态常量属性且通过静态内部类构造的单例
 */
class Singleton3 {
	private Singleton3() {
		super();
	}
	private static class SintletonClassInstance {
		private static final Singleton3 instance = new Singleton3();
	}
	
	public static Singleton3 getInstance() {
		return SintletonClassInstance.instance;
	}
		
	public static void main(String[] args) {
		for (int i = 0; i < 100; i++) {
			Runnable run = () -> System.out.println(Singleton3.getInstance());
			run.run();
		}
	}
}
  • 这种写法是在静态属性的基础上,通过静态内部类懒加载的特性除去了无用内存开销的弊端

      至于选择哪种方案,取决你自己的喜好和习惯
 
 
 
 
 
 

猜你喜欢

转载自blog.csdn.net/weixin_37481769/article/details/85336695