单例主要有两种加载思想:
1.强调响应速度和反应时间 - 非延迟加载,又称饿汉式
2.强调资源利用效率 - 延迟加载,又称懒汉式
非延迟加载单例类 饿汉式
public class Singleton {
private Singleton() {}
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
同步延迟加载 懒汉式
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检测同步延迟加载
为避免非延迟加载瓶颈,需要对 instance进行第二次检查。因为这里的同步只需在第一次创建实例时才同步,创建成功以后获取实例时不再需要获取同步锁。为避免JIT编译器对代码的指令重排序,使用volatile关键字。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
使用ThreadLocal修复双重检测
借助于ThreadLocal
,将临界资源(需要同步的资源)线程局部化。具体到就是将双重检测的第一层检测条件 if (instance == null)
转换为了线程局部范围内来操作。
这里的ThreadLocal也只是用作标示而已,用来表示每个线程是否已访问过。如果访问过,则不再需要走同步块,这样就提高了效率。
但是ThreadLocal
在Java1.4
以前的版本都较慢,但这与volatile
相比却是安全的。
public class Singleton {
private static final ThreadLocal perThreadInstance= new ThreadLocal();
private static Singleton singleton ;
private Singleton() {}
public static Singleton getInstance() {
if (perThreadInstance.get() == null){
// 每个线程第一次都会调用
createInstance();
}
return singleton;
}
private static final void createInstance() {
synchronized (Singleton.class) {
if (singleton == null)
singleton = new Singleton();
}
perThreadInstance.set(perThreadInstance);
}
}
延迟加载
类的延迟加载:
public class Singleton {
private Singleton() {}
public static class Holder {
static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}