一:单列模式的定义
确保某个类只有一个实例。
二:线程安全
说起单列模式,不得不提线程安全,那么什么是线程安全?我们通过几种方式来说说
1.多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。就是线程安全的。
2.你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
3.一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,就是线程安全的。
三:单列模式的几种写法
1:恶汉单列
优点:写法比较简单,在类装载的时候就完成实例化。避免了线程同步问题,天生线程安全。
缺点:类装载的时候就完成实例化,一定程度上会造成资源的浪费。
public class Singleton {
private static final Singleton INSTANCE=new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return INSTANCE;
}
}
2:懒汉单列
优点:只有使用时,才被实例化。
缺点:效率低,第一次加载需要实例化,反应稍慢。每次调用getInstance方法都会进行同步,消耗不必要的资源。
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
3:双重检查单列
优点:资源利用率高,效率高。
缺点:第一次加载时反应稍慢。某些情况下失效。
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
4:静态内部类单列(推荐使用)
优点:保证了线程安全,资源利用率高,效率高。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
5:枚举单列(推荐使用)
优点:写法简单,任何情况下都是一个单列。前面几种方法会在反序列化的时候,重新创建对象。
缺点:实例化一个单例类的时候,必须要记住使用相应的获取对象的方法。并且枚举在SDK1.5中才添加。
public enum SingletonEnum {
INSTANCE;
public void doSomething() {
}
}
五:其他方式
除了以上方式,还可以使用容器形式实现单列
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<>();
private SingletonManager() {
}
public static void putObject(String key, String instance){
if(!objMap.containsKey(key)){
objMap.put(key, instance);
}
}
public static Object getObject(String key){
return objMap.get(key);
}
}
四:备注
不管哪种方式实现,核心原理都是讲构造函数私有化,通过静态方法获取一个唯一的实例,且保证线程安全、防止反序列化导致重新生成实例对象等问题。
实现方式取决项目本身。
另外,附上防止反序列化时重新生成对象方法:
private Object readResolve() throws ObjectStreamException {
return instance;
}