为什么需要单例?
/**
* 饿汉式(静态常量)(可用)
*/
public class Singleton1 {
// 由于加了static关键字,根据JVM的规定,在类加载的时候就会完成INSTANCE的实例化,这样就避免了线程同步问题
private final static Singleton1 INSTANCE = new Singleton1();
// 构造函数是私有的
private Singleton1() {
}
public static Singleton1 getInstance() {
return INSTANCE;
}
}
/**
* 饿汉式(静态代码块)(可用)
*/
public class Singleton2 {
private final static Singleton2 INSTANCE;
// 与上一种写法类似,由JVM保证了线程安全
static {
INSTANCE = new Singleton2();
}
// 构造函数是私有的
private Singleton2() {
}
public static Singleton2 getInstance() {
return INSTANCE;
}
}
/**
* 懒汉式(线程不安全)(不可用)
*/
public class Singleton3 {
private static Singleton3 instance;
// 构造函数是私有的
private Singleton3() {
}
public static Singleton3 getInstance() {
// 这种写法是线程不安全的,不可用
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
/**
* 懒汉式(线程安全)(不推荐用)
*/
public class Singleton4 {
private static Singleton4 instance;
// 构造函数是私有的
private Singleton4() {
}
// 这种写法虽然是线程安全的,但是效率太低,不推荐用
public synchronized static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
/**
* 懒汉式(线程不安全)(不可用)
*/
public class Singleton5 {
private static Singleton5 instance;
// 构造函数是私有的
private Singleton5() {
}
public static Singleton5 getInstance() {
// 这种写法并不是线程安全的,不可用
if (instance == null) {
synchronized (Singleton5.class) {
instance = new Singleton5();
}
}
return instance;
}
}
优点:线程安全,延迟加载,效率较高。
/**
* 双重检查 + volatile(推荐用)
*/
public class Singleton6 {
// volatile防止重排序
private volatile static Singleton6 instance;
// 构造函数是私有的
private Singleton6() {
}
public static Singleton6 getInstance() {
// 双重检查保证线程安全
if (instance == null) {
synchronized (Singleton6.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
为什么要用 volatile?
新建对象 rs = new Resource()
实际上有 3 个步骤:
如下图所示,重排序会带来NPE问题(NullPointerException, 空指针异常),而使用 volatile 可以防止重排序。
/**
* 静态内部类(线程安全,懒加载)(推荐用)
*/
public class Singleton7 {
// 构造函数是私有的
private Singleton7() {
}
// 由JVM的规定可知,这种写法同时满足了线程安全和懒加载两个优点
private static class SingletonInstance {
private static final Singleton7 INSTANCE = new Singleton7();
}
public static Singleton7 getInstance() {
return SingletonInstance.INSTANCE;
}
}
单例模式的书写:
/**
* 枚举(线程安全,懒加载)(推荐用)
*/
public enum Singleton8 {
INSTANCE;
public void whatever() {
}
}
单例的使用:
Singleton8.INSTANCE.whatever();
哪种单例的实现方案最好?
Joshua Bloch 大神在《Effective Java》中明确表达过的观点:使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现 Singleton 的最佳方法。