1.定义
单例模式,顾名思义,在该设计模式中,一个类只能存在一个实例。不能有超过一个实例的情况。
下面的图片就是单例模式的类图:
2.应用场景
单例模式的应用场景还挺多的。举个例子吧
- windows系统的任务管理器
- 回收站
- 网站计数器
3. 单例模式的写法
3.1 懒汉式(线程不安全)
在我们学习单例模式的时候,第一次给出我们的代码就是下面这个,最简单的代码。但是它有一个问题就是线程不安全。多线程的情况下可能产生多个实例。
public class Singleton{
private static Singleton instance;
Singleton(){}
public static Singleton getInstance() {
if(instance==null) {
instance = new Singleton();
}
return instance;
}
}
3.2 懒汉式(线程安全)
为了改变上面单例模式的缺点,我们给getInstance()方法加上synchroized 关键字。具体代码如下:
public class Singleton{
private static Singleton instance;
Singleton(){}
//加入了synchronized关键字
public static synchronized Singleton getInstance() {
if(instance==null) {
instance = new Singleton();
}
return instance;
}
}
3.3 饿汉式
饿汉式的写法就是声明instance为static final类型的,在类加载的时候初始化类的实例。在getInstance()方法中直接返回类的实例。
public class Singleton{
private static final Singleton instance = new Singleton();
Singleton(){}
public static Singleton getInstance() {
return instance;
}
}
3.4 双重检验锁
双重检验锁是为了解决懒汉式(线程安全)的多线程不高效的问题,在synchronized代码块中,判断instance的状态是否为空,保证instance的多线程安全性。
public class Singleton{
private static Singleton instance;
Singleton(){}
public static Singleton getInstance() {
if(instance==null) {
synchronized(Singleton.class) {
if(instance==null) {
instance = new Singleton();
}
}
}
return instance;
}
}
但是这里有一个问题就是,如果执行以下的代码会产生单例生成不一致的情况。主要是在代码的第8行,当代码读取到instance不为null的时候,instance引用的对象可能还没有完成初始化。
instance = new Singleton();
这个代码可以分解为下面三行伪代码:
memory = allocate(); //1: 分配对象的内存空间
ctorInstance(memory); //2: 初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址。
在上述的代码中第二行和第三行可能会进行指令的重排序。
这就导致了问题的出现。
当我们把instance设置成volatile类型的时候,因为volatile禁止了指令的重排序所以类似问题得到了解决。
public class Singleton{
private volatile static Singleton instance;
Singleton(){}
public static Singleton getInstance() {
if(instance==null) {
synchronized(Singleton.class) {
if(instance==null) {
instance = new Singleton();
}
}
}
return instance;
}
}
3.5 静态内部类
在《Effective Java》中,推荐用此类方法。即保证多线程的安全性,又保证了高效性。
public class Singleton{
private static class SingletonHolder{
private static final Singleton INATANCE = new Singleton();
}
Singleton(){}
public static Singleton getInstance() {
return SingletonHolder.INATANCE;
}
}
3.6 枚举
public enum EasySingleton{
INSTANCE:
}
在枚举类型的单例模式中,是最简单的单例模式的写法,可以直接通过EasySingleton.INSTANCE来访问实例。
4. 总结
这里一共列举了六种单例模式的写法,准确的说是5种,这里推荐的是静态内部类来实现单例模式,在保证并发的高效性的情况下,又保证了线程安全。