详解单例设计模式的线程安全问题
2018-02-13 13:11
225 查看
1. 饿汉模式
/** * 饿汉模式 */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } public static void func() { System.out.println("this is a function!"); } }优点:
线程安全、绝对单例
缺点:
在多实例或者有其他静态方法时,instance在没有使用它的时候就已经加载好了。
例如调用Singleton.func(),这个方法中并没有用到instance,但是instance也被初始化了。若该对象很大,则在没使用时就加载很浪费内存。
不能防止反序列化、反射产生新的实例。
2. 懒汉模式,延时加载
/** * 懒汉模式 */ public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (null == instance) { // ① instance = new Singleton(); } return instance; } }优点:
instance在没有使用时不用加载,节省内存。
缺点:
线程不安全;
线程不安全原因:当多个线程执行到①时,都是发现instance为null,于是都去初始化了。
不能防止反序列化、反射产生新的实例。
3. 方法锁
/** * 方法锁 */ public class Singleton { private static Singleton instance = null; private Singleton() { } public static synchronized Singleton getInstance() { if (null == instance) { instance = new Singleton(); } return instance; } }优点:
线程安全,绝对单例
缺点:
加锁导致程序并发性降低;
不能防止反序列化、反射产生新的实例。
4. 双重检查锁 DCL
/** * 双重检查锁DCL */ public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (null == instance) { // ① synchronized (Singleton.class) { if (null == instance) { // ② instance = new Singleton(); // ③ } } } return instance; } }优点:
大部分时候线程安全,相比方法锁程序并发性较高。
缺点:
线程并非绝对安全;
不能防止反序列化、反射产生新的实例。
线程不安全原因:
Java的乱序执行、初始化对象需要时间。
对于语句③,JVM在执行时大致做了下述三件事:
a. 在内存中分配一块内存
b. 调用构造方法
c. 将内存的地址指向instance变量。(执行这一步后,instance != null)
如果按照abc的顺序执行也不会有什么问题。但由于Java乱序执行的机制,有可能在真实情况下执行顺序为acb。
假设t1、t2是两个线程。t1执行到①时,发现为null,于是执行到语句③,先执行a,再执行c,在还没有执行b时,时间片给了t2。这时,由于instance已经分配了地址空间,instance不为null了。所以t2在执行到语句①后直接return instance,获得了这个还没有被初始化的对象,然后在使用时就报错了。
也就是说,在使用DCL时,有可能得到了实例的正确引用,但却访问到其成员变量的不正确值。
5. 双重检查锁DCL与volatile的联用
/** * 双重检查锁DCL与volatile的联用 */ public class Singleton { private static volatile Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (null == instance) 4000 { synchronized (Singleton.class) { if (null == instance) { instance = new Singleton(); // ③ } } } return instance; } }优点:
线程安全、绝对单例
缺点:
代码看起来有点乱;
不能防止反序列化、反射产生新的实例
线程安全的原因:
我纳闷这个很久,后来发现我只关注了volatitle的一个特性:对变量的读取每次都是从主存中读取。volatitle还有另外一个特性:volatile屏蔽指令重排序。也就是说,执行语句③时,肯定是按照abc的顺序执行的(参见四)。
6. 静态内部类 Initialization On Demand Holder
/** * 静态内部类 Initialization On Demand Holder */ public class Singleton { private static class SingletonHolder{ static final Singleton instance = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return SingletonHolder.instance; } }优点:
线程安全,绝对单例
缺点:
不能防止反序列化、反射产生新的实例
和饿汉模式的区别:饿汉模式会在没有使用它的时候就已经加载实例。
线程安全的原因:Java的运行机制保证了static修饰的类、对象仅被初始化一次。
7. 枚举类
public enum Singleton { instance { String name = "this is name"; public void setName(String name) { this.name = name; } public String getName() { return name; } }; public abstract void setName(String name); public abstract String getName(); }优点:
线程安全、绝对单例、可以防止因为反序列化、反射产生新的实例
缺点:
和饿汉模式类似,会造成内存浪费
本文转载于一篇博客http://blog.csdn.net/h28496/article/details/49884469,写得比较全面具体,笔者转载之,推荐学习。
相关文章推荐
- 设计模式(一)--深入单例模式(涉及线程安全问题)
- [置顶] Java 多线程学习笔记(十一) 单例设计模式(延迟加载/懒汉模式)DCL解决线程安全问题
- Java设计模式-单例模式及线程安全问题
- 单例设计模式的线程安全问题(转)
- 单例设计模式及懒汉式线程安全问题
- 解决单例设计模式中懒汉式线程安全问题
- 完美解决单例设计模式中懒汉式线程安全的问题
- 单例设计模式:饿汉式、懒汉式、枚举方式。以及涉及到的线程安全问题和反射机制问题。
- 解决单例设计模式中懒汉式线程安全问题
- java设计模式--解决单例设计模式中懒汉式线程安全问题
- 设计模式____单列模式(懒加载,常加载,双检索,线程安全问题)
- 解决单例设计模式中的懒汉设计模式存在线程安全问题(并且在一定程度上提高效率)的一种解决方法
- Java——单例设计模式中懒汉式并发访问的安全问题
- java 23种设计模式详解01-02-03-工厂
- Java开发中的23种设计模式详解(转)
- 23种设计模式模式详解 Java UML类图小知识(一)
- Singleton 模式的问题探究及多线程下设计问题
- Java开发中的23种设计模式详解
- Java开发中的23种设计模式详解
- 设计模式-04-行为型模式详解