(1)饱汉模式
1.1基础饱汉模式
程序启动时并不着急进行初始化单例,等调用到获取实例的方法时再进行初始化,即为“懒加载”。核心为“懒加载”,优点是启动速度快、节省资源,缺点是写起来麻烦且线程不安全。
public class Singleton1 {
private static Singleton1 singleton = null;
private Singleton1() {
}
public static Singleton1 getInstance() {
if (singleton == null) {
singleton = new Singleton1();
}
return singleton;
}
}
1.2饱汉模式-变种1
在getInstance()方法上添加synchronized关键字修饰,这样能保证真正的线程安全,但是缺点是并发性能极差,退化成串行,就算初始化以后,synchronized的锁也无法避开,可以在性能不敏感场景下使用。
public class Singleton1_1 {
private static Singleton1_1 singleton = null;
private Singleton1_1() {
}
public synchronized static Singleton1_1 getInstance() {
if (singleton == null) {
singleton = new Singleton1_1();
}
return singleton;
}
}
1.3饱汉模式-变种2
双重检查锁(Double Check Lock,简称DCL),表面上懒加载+线程安全,但是由于指令重排,线程仍然不安全。
public class Singleton1_2 {
private static Singleton1_2 singleton = null;
public int f1 = 1; // 触发部分初始化问题
public int f2 = 2;
private Singleton1_2() {
}
public static Singleton1_2 getInstance() {
// may get half object
if (singleton == null) {
synchronized (Singleton1_2.class) {
if (singleton == null) {
singleton = new Singleton1_2();
}
}
}
return singleton;
}
}
1.4饱汉模式-变种3
在变种2的基础上在instance上增加volatile关键字。在多线程环境下,变种3更适合性能敏感的场景。
public class Singleton1_3 {
private static volatile Singleton1_3 singleton = null;
public int f1 = 1; // 触发部分初始化问题
public int f2 = 2;
private Singleton1_3() {
}
public static Singleton1_3 getInstance() {
if (singleton == null) {
synchronized (Singleton1_3.class) {
// must be a complete instance
if (singleton == null) {
singleton = new Singleton1_3();
}
}
}
return singleton;
}
}
(2)饿汉模式
在类加载时就初始化单例,在调用到获取实例的方法时直接返回即可。优点是天生的线程安全(得益于类加载机制),缺点是如果一直不使用单例的话比较耗费资源。
public class Singleton2 {
private static final Singleton2 singleton = new Singleton2();
private Singleton2() {
}
public static Singleton2 getInstance() {
return singleton;
}
}
值得注意的时,单线程环境下,饿汉与饱汉在性能上没什么差别;但多线程环境下,由于饱汉需要加锁,饿汉的性能反而更优。
(3)Holder模式
Holder模式的核心为饿汉模式的静态变量,保证了线程安全,同时通过静态Holder类持有真正实例,间接实现了懒加载。
public class Singleton3 {
private static class SingletonHolder {
private static final Singleton3 singleton = new Singleton3();
private SingletonHolder() {
}
}
private Singleton3() {
}
/**
* 勘误:多写了个synchronized。。
public synchronized static Singleton3 getInstance() {
return SingletonHolder.singleton;
}
*/
public static Singleton3 getInstance() {
return SingletonHolder.singleton;
}
}
(4)枚举模式
代码量比饿汉模式更少,但用户只能直接访问实例Singleton4.SINGLETON,牺牲了静态工厂方法的优点,无法实现懒加载。
public enum Singleton4 {
SINGLETON;
}