Loading

单例模式

定义
单例模式是指在任何情况下有且只有一个实例,并且提供一个全局访问点。
特点
私有化构造器,公共静态方法。

饿汉式单例

饿汉式单例在类加载的时候就立即初始化,并且创建单例对象,绝对线程安全。
  • 写法一
public class HungrySingleton {
	
	private static final HungrySingleton hungrySingleton = new HungrySingleton();
	
	private HungrySingleton() {}
	
	public static HungrySingleton getInstance() {
		return hungrySingleton;
	}
}
  • 写法二
public class HungryStaticSingleton {
	
	private static HungryStaticSingleton HUNGRY_SINGLETON;
	
	static {
		HUNGRY_SINGLETON = new HungryStaticSingleton();
	}
	
	private HungryStaticSingleton() {}
	
	public static HungryStaticSingleton getInstance() {
		return HUNGRY_SINGLETON;
	}
}

懒汉式单例

对象在第一次被调用时才初始化。
  • 方式一:以下方式存在线程安全隐患,在单线程环境下可以保证单例,但是在多线程下,可能会创建多个实例对象。
public class LazySingleton {
	
	private static LazySingleton lazySingleton;
	
	private LazySingleton() {}
	
	public static LazySingleton getInstance() {
		if (lazySingleton == null) {
			lazySingleton = new LazySingleton();
		}
		return lazySingleton;
	}
}
  • 方式二:利用synchronized关键字解决线程安全问题,但是在并发情况下,所有的线程都被阻塞,会造成性能问题。
public class LazySyncSingleton {
	
	private static LazySyncSingleton lazySyncSingleton;
	
	private LazySyncSingleton() {}
	
	public synchronized static LazySyncSingleton getInstance() {
		if (lazySyncSingleton == null) {
			lazySyncSingleton = new LazySyncSingleton();
		}
		return lazySyncSingleton;
	}
}

  • 方法三:双重检查锁创建单例,也是最常用的方式。此方式解决了饿汉式单例资源浪费问题,也解决了synchronized性能问题。
public class DoubleCheckLazySingleton {
	
	private static DoubleCheckLazySingleton doubleCheckLazySingleton;
	
	private DoubleCheckLazySingleton() {}
	
	public static DoubleCheckLazySingleton getInstance() {
		if (doubleCheckLazySingleton == null) {
			synchronized (DoubleCheckLazySingleton.class) {
				if (doubleCheckLazySingleton == null) {
					doubleCheckLazySingleton = new DoubleCheckLazySingleton();
				}
			}
		}
		return doubleCheckLazySingleton;
	}
}

反射破坏单例

以上所有单例写法,即使存在私有化构造器,但是在反射通过构造器创建对象时,还是会创建出新的对象。

public class ReflexDamageSingletonTest {

	public static void main(String[] args) throws Exception {
		Class<?> clazz = DoubleCheckLazySingleton.class;
		Constructor constructor = clazz.getDeclaredConstructor();
		constructor.setAccessible(true);
		Object instance1 = constructor.newInstance();
		Object instance2 = constructor.newInstance();
		System.out.println(instance1 == instance2);
	}
}

以上反射执行的结果肯定是false,虽然正常开发没人闲的去干这种事,但是为了以防万一,可以通过以下方式来防止别人通过反射破坏单例。利用内部类加载机制,在反射创建对象时,判断内部类是否已经创建好了对象。

public class InnerClassSingleton {
	
	private InnerClassSingleton(){
		if (InnerClass.INNER_CLASS_SINGLETON != null) {
			throw new RuntimeException("不允许创建多个单例对象");
		}
	}
	
	public static InnerClassSingleton getInstance() {
		return InnerClass.INNER_CLASS_SINGLETON;
	}
	
	private static class InnerClass {
		private static final InnerClassSingleton INNER_CLASS_SINGLETON = new InnerClassSingleton();
	}
}
posted @ 2021-03-24 19:42  风吹屁屁疼  阅读(57)  评论(0编辑  收藏  举报