/**
* 单例模式,懒汉式,线程不安全
*/
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
/**
* 单例模式,饿汉式,线程安全
*/
public class Singleton2 {
private static Singleton2 instance = new Singleton2();
private Singleton2() {}
public static Singleton2 getInstance() {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
/**
* 单例模式,饿汉式,线程安全,多线程环境下效率不高
*/
public class Singleton3 {
private static Singleton3 instance = null;
private Singleton3() {}
public static synchronized Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
/**
* 单例模式,饿汉式,由 static 块保证线程安全
*/
public class Singleton4 {
private static Singleton4 instance;
static {
instance = new Singleton4();
}
private Singleton4() {}
public static Singleton4 getInstance() {
return instance;
}
}
/**
* 单例模式,懒汉式,使用静态内部类,线程安全【推荐】
*/
public class Singleton5 {
private class SingletonHolder {
private static final Singleton5 INSTANCE = new Singleton5();
}
private Singleton5() {}
public static Singleton5 getInstance() {
return SingletonHolder.INSTANCE;
}
}
/**
* 使用枚举方式,线程安全【推荐】
*
* 枚举自己处理序列化
*/
public enum Singleton6 {
//调用 Singleton6.INSTANCE.whateverMethod()
INSTANCE
public void whateverMethod() {
}
}
/**
* 使用双重校验锁,线程安全【推荐】
*/
public class Singleton7 {
private volatile static Singleton7 instance = null;
private Singleton7() {}
public static Singleton7 getInstance() {
if (instance == null) {
synchronized (Singleton7.class) {
if (instance == null) {
instance = new Singleton7();
}
}
}
return instance;
}
}
推荐:静态内部类、枚举、双重校验锁
最好的单例模式:枚举
使用枚举实现单例模式是最好的方法,因为
写法简单
public enum Singleton6 {
//调用 Singleton6.INSTANCE.whateverMethod()
INSTANCE
public void whateverMethod() {
}
}
枚举实例创建是线程安全的
当一个Java类第一次被真正使用到的时候,静态资源被初始化。Java类的加载和初始化过程都是线程安全的。enum类型会被编译器编译成class T extends Enum
的类。所以,创建一个enum类型是线程安全的。
枚举自己处理序列化
普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新new出来的,所以这就破坏了单例。但是,枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。
关于枚举类的序列化和反序列化:在序列化的时候Java仅仅是将枚举对象的属性输出到结果中,反序列化的时候则是通过java.lang.Enum
的valueOf
方法来根据名字查找枚举对象。同时,编译器不允许对这种序列化机制进行定制,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
volatile 关键字的含义: - 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,新值对其他线程是立即可见的 - 禁止进行指令重排序
volatile 关键字在本实现的主要作用:禁止进行指令重排序。因为类初始化分两步: volatile 的不足:无法保证原子性,所以要结合synchronized实现
volatile 的使用条件: - 对变量的写操作不依赖于当前值 - 该变量没有包含在具有其他变量的不变式中 (a <= b)