定义
应用场景
实现
饿汉式
线程安全,所有对象类在加载时实例化,如果系统中单例对象比较多,不论是否在使用,都占着空间,造成内存浪费
demo1
public class Singleton {
private static final Singleton instance = new Singleton();
// 私有化构造方法,避免直接创建对象
private Singleton() {}
// 提供获取对象的方法
public static Singleton getInstance() {
return instance;
}
}
demo2
这里使用静态代码块的机制实现
public class HungrySingleton {
private static final HungrySingleton instance;
static {
instance = new HungrySingleton();
}
private HungrySingleton() {}
public static HungrySingleton getInstance() {
return instance;
}
}
懒汉式
懒汉式,在使用时创建对象,避免了饿汉式单例模式造成的内存浪费
demo1
这个例子存在线程安全问题,例:threadA在getInstance()时,instance为空,而这时,threadB也调用getInstance()方法,instance也为空,threadA和threadB分别创建了一个对象实例
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
demo2
解决懒汉式demo1中的线程安全问题,这种方式效率较低,同一时刻仅有一个线程执行该方法
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
// 直接同步整个方法,简单粗暴
public synchronized static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
demo3 双检锁
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
// 直接同步整个方法,简单粗暴
public static LazySingleton getInstance() {
if (instance == null) {
synchronized(LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
静态内部类
线程安全,懒汉式,避免了内存浪费和synchronized性能问题
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton() {}
private static LazyStaticInnerClassSingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton ();
}
}
枚举
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
拓展
反射破坏单例
在实现单例类的时候,我们私有化了构造方法,这样理论上我们就没有办法通过构造方法直接创建一个新的对象。private 修饰的构造方法仅能在类内调用。
不过Java提供了强大的工具 - 反射,通过反射,我们可以直接获取到对象内部的属性、方法等,包括私有属性、方法。
try {
Class<?> clazz = LazyInstance.class;
// 获取类的构造方法
Constructor c = clazz.getDeclaredConstructor(null);
// 强制访问
c.setAccessible(true);
// 通过反射创建两个对象
Object instance1 = c.newInstance();
Object instance2 = c.newInstance();
System.out.println(instance1 == instance2);//false
} catch(Exception e) {
e.printStackTrace();
}
解决反射破坏单例的问题
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton() {
if (LazyHolder.INSTANCE != null) {
throw new RuntimeException("Cann't create multiple instance.");
}
}
private static LazyStaticInnerClassSingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton ();
}
}
序列化破坏单例
重写readResolve()方法
网友评论