7种单例模式的写法及其分析

一、什么是单例模式?

单例模式,顾名思义就是 一个类只能有一个实例,并且在整个项目中都能访问到这个实例。

二、Java中单例模式的7种写法及其分析

写法一

public class Singleton {
	
	private Singleton(){}
	
	private static Singleton instance = new Singleton();
	
	public static Singleton getInstance(){
		return instance;
	}
}

最简单的写法,缺点在于实例在类初始化的时候就创建了,如果在整个项目中都没有使用到该类,就会创建内存空间的浪费。

写法二

public class Singleton {
	
	private Singleton(){}
	
	private static Singleton instance;
	
	public static Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();		
		}
		return instance;
	}
}

解决了写法一在类初始化的时候就创建实例的问题,然而只能在单线程中使用,在多线程中使用如果多个线程同时进入if语句中,就可能出现创建多个实例的问题。

写法三

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

解决了写法二可能出现的问题,可以在多线程中使用。缺点在于synchronized关键字会强制一次只能让一个线程进入方法中,其他线程不得不阻塞等待该线程退出方法。

写法四

public class Singleton {
	
	private Singleton(){}
	
	private static Singleton instance;
	
	public static Singleton getInstance(){
		if(instance == null){
			synchronized(Singleton.class){
				if(instance == null){
					instance = new Singleton();		
				}
			}
		}
		return instance;
	}
}

解决了写法四出现的问题,缺点在于 JVM 在执行对引用赋值时并不是一个原子操作。具体是首先在堆中为 Singleton 分配内存空间,然后对 Singleton 执行初始化操作, 最后再将引用变量指向该实例所对应的内存地址以完成赋值。而 JVM 对这一步进行了优化,使得步骤二和步骤三可以不按顺序执行。所以就有可能出现一个线程在执行了步骤一之后执行了步骤三,然后另一个线程调用了该方法,此时 instance 引用变量不为空,然而 Singleton 实例还没有完成初始化,就会造成报错。

写法五

public class Singleton {
	
	private Singleton(){}
	
	private static volatile Singleton instance;
	
	public static Singleton getInstance(){
		if(instance == null){
			synchronized(Singleton.class){
				if(instance == null){
					instance = new Singleton();		
				}
			}
		}
		return instance;
	}
}

volatile关键字能够禁止指令重排,保证在写操作没有完成之前不能调用读操作。

写法六

public class Singleton {
	
	private Singleton(){}
	
	private static class SingletonHolder {
		private static Singleton instance = new Singleton();
	}
	
	public static Singleton getInstance(){
		return SingletonHolder.instance;
	}
}

这种写法的好处是充分利用了静态内部类的特点,它的初始化操作跟外部类是分开的。在没有调用 getInstance() 方法之前,静态内部类不会进行初始化,在第一次调用该方法后就生成了唯一一个实例。

写法七

public enum Singleton {
	
	INSTANCE;
	
	public void fun(){}
	
}

推荐写法,简单高效。充分利用枚举类的特性,只定义了一个实例,且枚举类是天然支持多线程的。

  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值