单例模式的设计,需要分是否延迟加载.
从速度和反应时间角度来讲,非延迟加载(又称饿汉式)好;从资源利用效率上说,延迟加载(又称懒汉式)好。
单例模式的几种写法:
//第一种:非延迟加载单例类
//饿汉式, 这样写也是线程安全的.
`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 进行第二次检查,目的是避开过多的同步(因为这里的同步只需在第一次创建实例时才同步,一旦创建成功,以后获取实例时就不需要同获取锁了
//JDK5.0以后版本的instance为volatile才能使用:
//双重检测锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型,
// 内存模型允许所谓的“无序写入”,简单理解就是Java编译代码到JVM虚拟机时,是没有顺序的.
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {// 1
if (instance == null) {// 2
instance = new Singleton();// 3
}
}
}
return instance;
}
}
第四种:使用ThreadLocal修复双重检测
//借助于ThreadLocal,将临界资源(需要同步的资源)线程局部化,具体到本例就是将双重检测的第一层检测条件 if (instance == null) 转换为了线程局部范围内来作。这里的ThreadLocal也只是用作标示而已,用来标示每个线程是否已访问过,如果访问过,则不再需要走同步块,这样就提高了一定的效率。但是ThreadLocal在1.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;
}
}
第六种: 使用枚举
//枚举在JVM虚拟机中, 默认是单例的, 并且是线程安全的. 这种方式也是最简单的
public class MyClass {
static enum Singleton {
instance;
}
}
第七种: 使用容器
//这种使用容器的单例模式, 在Android系统服务里面有使用.
public class MyClass {
static class SingletonManager{
private static Map objectMap = new HashMap<>();
public static void registerService(String key,Object instance) {
if (!objectMap.containsKey(key)) {
objectMap.put(key, instance);
}
}
public static Object getService(String key) {
return objectMap.get(key);
}
}
}
参考资料: https://www.iteye.com/topic/652440



