- 优点:
- 只有一个实例,减少内存开支,减少创建销毁和初始化的性能开销
- 可以避免资源的多重占用,一种资源操作只有一个实例进行操作
- 提供全局访问点,优化和共享资源访问
- 缺点:
- 单例模式要求自行实例化,并提供单一实例,因此单例对象的扩展只能对其自身进行修改
- 单例模式与开闭原则、单一职责原则冲突
饿汉式单例
// 饿汉式单例,类文件初始化过程中完成单例实例化
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
// static {
// instance = new HungryStaticSingleton();
// }
private HungrySingleton(){}
public static HungrySingleton getInstance(){return instance;}
}
懒汉式单例
双重校验锁
public class LazyDoubleCheckSingleton {
private **volatile** static LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
if(instance == null){
synchronized (LazyDoubleCheckSingleton.class){
if(instance == null){
instance = new LazyDoubleCheckSingleton();
}
}
}
return instance;
}
}
volatile 防止指令重排序:使INVOKESPECIAL(初始化指令) 先于 PUTSTATIC(引用赋值指令)
// t = new Test(); // NEW指令,创建指定类型的对象实例、对其进行默认初始化,并且将指向该实例的一个引用压入操作数栈顶; NEW Test // invokespecial会消耗掉操作数栈顶的引用作为传给构造器的“this”参数,所以如果我们希望在invokespecial调用后在操作数栈顶还维持有一个指向新建对象的引用,则使用DUP指令复制操作栈顶数据并入栈 DUP // 调用<init>方法,<init>方法为实例方法,需消耗栈顶的一个对象引用 INVOKESPECIAL Test.<init> ()V // 将引用赋值给静态变量Test.t; PUTSTATIC Test.t : LTest;
静态内部类
/*
* 内部静态类实现懒汉式单例
*
* 由于java类的加载机制,在加载LaztStaticInnerClassSingleton时,并不会加载LazyHolder类
* 只有当调用getInstance方法时,才会对LazyHolder进行加载,从而实现懒加载
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton(){}
public static LazyStaticInnerClassSingleton getInstance(){
return LazyHolder.instance;
}
private static class LazyHolder{
private static final LazyStaticInnerClassSingleton instance = new LazyStaticInnerClassSingleton();
}
}
枚举式单例
// 枚举式单例(饿汉) - 可避免反射破坏 - 枚举类型不允许反射创建实例
public enum EnumSingleton {
instance;
// 其实这段方法并没有太大意义,不过习惯使用getInstance表示单例,直接用EnumSingleton.instance也可。
public static EnumSingleton getInstance(){return instance;}
// 以下为应用属性和代码
private int index = 0;
public void print(){
System.out.println(index);
}
}
// 枚举类的实际实现:
public final static enum Ltop/kiqi/design/pattern/singleton/hungry/EnumSingleton; instance
// synthetic 标记,由编译器生成的属性/方法/类
private final static synthetic [Ltop/kiqi/design/pattern/singleton/hungry/EnumSingleton; $VALUES
static{
instance = new EnumSingleton("instance",0);
$VALUES = new EnumSingleton[]{instance};
}
容器式单例(IOC)
public class ContainerSingleton{
private static ConcurrentHashMap<String,Object> ioc = new ConcurrentHashMap<>();
private ContainerSingleton(){
throw new RuntimeException("非法访问!");
}
// 方法一: 双重校验锁
public static Object getInstance(String className){
if(!ioc.contains(className)){
synchronized (ContainerSingleton.class){
if(!ioc.contains(className)){
try{
Object instance = Class.forName(className).newInstance();
ioc.put(className,instance);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
return ioc.get(className);
}
// 方法二: 利用ConcurrentHashMap的putIfAbsent
public static Object getInstance(String className){
if(!ioc.contains(className)){
try {
ioc.putIfAbsent(className, Class.forName(className).newInstance());
}catch (Exception e){
e.printStackTrace();
}
}
return ioc.get(className);
}
}
线程私有式单例
// 线程级单例 - 使用ThreadLocal为每个线程私有维护一份实例对象
public class ThreadLocalSingleton {
private static ThreadLocal<ThreadLocalSingleton> threadLocalSingleton = ThreadLocal.withInitial(ThreadLocalSingleton::new);
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocalSingleton.get();
}
}
避免破坏单例
避免反射破坏单例
// 构造方法中添加判断
private Singleton(){
if(instance != null){
throw new RuntimeException("非法访问!");
}
}
避免反序列化破坏单例:
// 实现方法 - 当调用java反序列化方法(ObjectInputStream.readObject())时,会判断该方法,如果存在,则使用该方法的返回值。
public Object readResolve(){
return instance;
};