众所周知,在java中单例模式实现有7种,分别如下:
1、懒汉模式(线程不安全)
public class Singleton{
private static Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
if(instance == null ){
instance = new Singleton();
}
return instance;
}
}
这种模式采用懒加载lazy loading但是在多线程下不安全
2、懒汉模式(线程安全)
public class Singleton{
private static Singleton instance;
private Singleton(){};
public static synchronized Singleton getInstance(){
if(instance == null ){
instance = new Singleton();
}
return instance;
}
}
懒加载,线程安全,但效率极其低下,同步锁锁的是对象,每次取对象都加锁,因此效率低下
3、饿汉模式
public class Singleton{
private static Singleton instance= new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
这种方式基于classloader机制避免了多线程同步问题,不过instance在类装载时就实例化,没有达到懒加载效果
4、饿汉模式变种
public class Singleton{
private static Singleton instance = null;
static{
instance= new Singleton();
}
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
等同于第三种
5、静态内部类
public class Singleton{
private Singleton(){};
public static synchronized Singleton getInstance(){
return SingletonInnerClass.instance;
}
private static class SingletonInnerClass(){
private static Singleton instance = new Singleton();
}
}
利用classloader机制保证初始化实例时只有一个实例,与第三种第四种差别是达到了懒加载效果(第三种第四种方法只要singleton类被装载了那么instance就会被实例化,而这种方式SingletonInnerClass类没有被主动使用,只有被显示调用getInstance时才会显示装载SingletonInnerClass类,从而实现懒加载)在实例化instance很消耗资源,想让他延迟加载,另外不希望在singleton类加载时就实例化,这种方式就显得很合理 。推荐使用
6、双重校验锁
public class Singleton{
private static Singleton instance;
private Singleton(){};
public static synchronized Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
不管性能如何优越,使用了synchronized修饰符,就会对性能多少造成影响
7、枚举
public enum SIngleton{
INSTANCE;
public void whateverMethod(){}
}
这种方式是Effective Java提倡方式,不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象,但因为可读性原因不推荐
由此七方法也介绍完毕,但也有几个问题需要反思
1、如何在反射状态下保证单例?
2、如何在反序列化中保证单例?
对于第一个问题我们可以采取以下方式
public class Singleton3{
private static boolean initialized = false;
private Singleton3(){
synchronized(Singleton3.class){
if(initialized == false){
initialized = !initialized ;
}else{
throw new RuntimeException("单例已破坏");
}
}
}
static class SingletonHolder{
private static final Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance(){
return SingletonHolder.instance;
}
}
这就保证了反射无法破坏其单例
对于第二个问题我们需要提供readResolve()方法的实现,来代替从流中读取对象,这就确保了序列化和反序列化过程中没人可以创建新的实例
public class Singleton4 implements Serializable{
private static boolean initialized = false;
private Singleton4(){
synchronized(Singleton4.class){
if(initialized == false){
initialized = !initialized ;
}else{
throw new RuntimeException("单例已破坏");
}
}
}
static class SingletonHolder{
private static final Singleton4 instance = new Singleton4();
}
public static Singleton4 getInstance(){
return SingletonHolder.instance;
}
private Object readResolve(){
return getInstance();
}
}
本文给出了多种单例模式,一般从静态内部类和保证反射,序列化状态下单例选择,根据实际情况三选一即可,并不是非要选择最后一种作为单例实现