当我们使用线程池,缓存,注册表和日志等这些对象时,是否疑惑过,我们那么多个类使用的这些对象是同一个对象实例吗?答案显而易见——如果不是的话,那么这些功能将面临着无数的问题,这就是单件模式。
事实上,想让一个对象全局共享,static关键字也起着重要的作用,但是如果我们不是经常使用这个对象且该对象十分消耗资源的话,要知道这个对象是一开始就会被出创建的,那么是对我们系统资源的极大浪费,而单件模式可以克服这个缺点。
单件模式
定义:确保一个类只有一个实例变量,并提供一个全局访问点。
例子
我们先看一个最经典的单例实现:
package singleton;
/**
* 最经典的单例
*/
public class Singleton {
//静态私有变量,定义唯一实例(自身)
private static Singleton instance;
//必须是私有的构造器
private Singleton(){}
//对外暴露的访问点
public Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
注意这几个关键的地方:
- 私有的静态唯一实例(private static Singleton instance)
- 私有构造器(private Singleton(){})
- 对外暴露的访问点(public Singleton getInstance()}
并发访问
但是如果出现了并发访问,那么这个模式这样做就会出现线程安全问题,有以下几个解决方案:
- 饿汉模式(立即加载)
- 对暴露点添加synchronisezd关键字
- 静态代码块
- 静态内置类负责实例化单例
- DCL检查机制
package singleton;
/**
* 最经典的单例
*/
public class Singleton {
//静态私有变量,定义唯一实例(自身)
private static Singleton instance;
//饿汉模式或者静态代码块
//private static Singleton instance = new Singleton();
static {
instance = new Singleton();
};
//必须是私有的构造器
private Singleton(){}
//对外暴露的访问点
public Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
// synchronized关键字
public synchronized Singleton getInstance1(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
//DCL检查机制
public static Singleton getInstance3() {
//断点1
if (instance == null) {
//断点2
synchronized (Singleton.class) {
//断点3
if (instance == null) {
//断点4
instance = new Singleton();
}
}
}
return instance;
}
}
请看我的多线程读书笔记博客最后单例部分:《Java多线程编程核心技术》读书笔记
源码在这里:我的github地址