想整理一些 java 并发相关的知识,不知道从哪开始,想起了单例模式中要考虑的线程安全,就从单例模式开始吧。
之前写过单例模式,这里再从新汇总补充整理一下,单例模式的多种实现。html
单例模式那件小事,看了你不会后悔
单例模式不是一件小事,快回来看看java
以前在第一篇文章说,单例模式的主要思想是:安全
这种说法看上去没错,但也好像不太准确。其实,就算外界能随意 new 出新的实例对象,但只要咱们保证咱们每次使用的对象是惟一的,就能够。并发
public class Singleton { private Singleton() { } private static Singleton sSingleton = new Singleton(); public static Singleton getInstance() { return sSingleton; } }
public class Singleton { private Singleton() { } public static final Singleton sSingleton = new Singleton(); }
将实例对象用 public static final
修饰,不提供公开方法获取实例,直接经过 Singleton.sSingleton
获取。函数
public class Singleton { private Singleton() { } private static Singleton sSingleton; public static Singleton getInstance() { if (sSingleton == null) { sSingleton = new Singleton(); } return sSingleton; } }
public class Singleton { private Singleton() { } private static Singleton sSingleton; public synchronized static Singleton getInstance() { if (sSingleton == null) { sSingleton = new Singleton(); } return sSingleton; } }
getInstance
都进行同步,形成没必要要的同步开销,这种模式通常不建议使用。public class Singleton { private Singleton() { } /** * volatile is since JDK5 */ private static volatile Singleton sSingleton; public static Singleton getInstance() { if (sSingleton == null) { synchronized (Singleton.class) { // 未初始化,则初始instance变量 if (sSingleton == null) { sSingleton = new Singleton(); } } } return sSingleton; } }
sSingleton = new Singleton() 不是一个原子操做。(XXX)故须加 volatile
关键字修饰,该关键字在 jdk1.5 以后版本才有。高并发
public class Singleton { private Singleton () { } private static class InnerClassSingleton { private final static Singleton sSingleton = new Singleton(); } public static Singleton getInstance() { return InnerClassSingleton.sSingleton; } }
优势:推荐使用。测试
public enum Singleton{ INSTANCE; // 其它方法 public void doSomething(){ ... } }
import java.util.HashMap; import java.util.Map; public class Singleton { private static Map<String, Object> objMap = new HashMap<String, Object>(); private Singleton() { } public static void registerService(String key, Object instance) { if (!objMap.containsKey(key)) { objMap.put(key, instance); } } public static Object getService(String key) { return objMap.get(key); } }
利用了 HashMap 容器 key 不可重复的特性。线程
前面的多种实现方法中,不少咱们按照构造方法私有化的思想来实现的,咱们知道,利用反射,仍然能够建立出新对象,这样在反射场景中,这种思想实现的单例模式就失效了,那么如何防止反射破坏单例模式呢?原理上就是在存在一个实例的状况下,再次调用构造方法时,抛出异常。下面以静态内部类的单例模式为例:code
public class Singleton { private static boolean flag = false; private Singleton(){ synchronized(Singleton.class) { if(flag == false) { flag = !flag; } else { throw new RuntimeException("单例模式被侵犯!"); } } } private static class InnerClassSingleton { private final static Singleton sSingleton = new Singleton(); } public static Singleton getInstance() { return InnerClassSingleton.sSingleton; } }
具体测试代码,见 单例模式不是一件小事,快回来看看htm
经过序列化能够讲一个对象实例写入到磁盘中,经过反序列化再读取回来的时候,即使构造方法是私有的,也依然能够经过特殊的途径,建立出一个新的实例,至关于调用了该类的构造函数。要避免这个问题,咱们须要在代码中加入以下方法,让其在反序列化过程当中执行 readResolve 方法时返回 sSingleton 对象。
private Object readResolve() throws ObjectStreamException { return sSingleton; }
有没有一种方式实现的单例模式在任何状况下都是一个单例呢?
—— 有。就是上面说的枚举单例。枚举,就能保证在任何状况下都是单例的,而且是线程安全的。