单例模式
1.单例模式属于创建型模式。确保一个类只有单个对象被创建,这个类提供了一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1.单例类只能有一个实例;
- 2.单例类必须自己创建自己唯一的实例;
- 3.单例类必须给所有其他对象提供这一个实例。
2.使用条件:当想控制实例的数目的时候。比如一个班只有一个班主任,再比如Windows操作文件的时候必须是唯一实例。
3.优点:在内存只有一个实例,避免了内存的开销(频繁创建和销毁实例)
4.缺点:没有接口,不可继承。
5.关键思想:构造函数私有。
实现方法:
1.懒汉式,线程不安全。
描述:不支持多线程。
1 | public class SingleObject{ |
2.懒汉式,线程安全。
描述:采用synchronize加锁保证单例,支持多线程。加锁影响效率
1 | public class SingleObject{ |
3.饿汉式,线程安全。
描述:不用加锁,但类加载时就初始化,会浪费内存。
1 | public class SingleObject{ |
4.双重校验锁,线程安全
描述:采用双锁机制,在保证线程安全的情况下有较高性能。
1 | public class SingleObject{ |
在网上查阅发现双重锁还是有隐患,在实例化对象那一行,发生的事情有:
A: 分配内存空间
B:初始化对象
C:将对象指向刚分配的空间
但有些编译器可能因为性能原因,BC重排序,会将BC调换顺序,顺序成了ACB。
假设有两个线程A,B:
时间 | Thread A | Thread B |
---|---|---|
1 | object=null | |
2 | 获取锁 | |
3 | 检查到object=null | |
4 | 为object分配内存 | |
5 | 将object指向内存空间 | |
6 | 检查到object!=null | |
7 | 访问object(此时还未完成初始化) | |
8 | 初始化object |
修改:在object前加上volatile关键字,重排序被禁止,所有的写操作都发生在读操作之后。
volatile作用:确保本条指令不会因为编译器的优化而省略。表示这个变量可能会意想不到的改变,这样,编译器就不会去假设这个变量的值了。
1 | public class SingleObject{ |