单件模式,就是在应用中只创建一个实例。
第一个版本
缺陷: 在多线程的情况下会出问题。
/**
* 1.0 版本
* 实现单件的基本定义
*/
public class Singleton(){
// 第一步: 将构造函数私有化
private Singleton(){}
// 第二步:定义一个单例的变量
private static Singleton singleton = null;
// 第三步: 提供一个公共的静态方法,使能够通过类获取到实例
public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
第一个版本,改进
/**
* 1.1 版本
* 实现
* 通过加锁,解决多线程中出现的问题
* 双重检查
*
* 补充: 在jvm中 : singleton = new Singleton(); 这句话做了三件事:
* 1) 给 singleton分配内容;
* 2) 调用 Singleton 的构造函数来初始化成员变量,形成实例;
* 3) 将singleton对象指向分配的内存空间(执行了这一步 singleton 才是非null了)
* jvm的指令存在重排序的优化,所以上面第二步和第三步的顺序不能保证,最终的执行
* 顺序可能是1-2-3,也可能是1-3-2。 如果是后者,则在3执行完,2未执行之前被第二个线程抢占了
* 这个时候 instance 已经是非null了,但是却没有初始化,所以第二个线程直接返回instance,
* 然后使用,顺利成章报错。
* 对此,只需要将 singleton 声明为 volatile (这个关键字只在jdk1.5之后有用,
* 1.5版本之前用这个变量也有问题,因为老版的Java内存模型有问题)
* 使用volatile 功能有两个:(1)这个变量不会在多线程中存在多个副本,直接从内存读取;
* (2) 这个关键字禁止指令重排序优化, 也就是说,在volatile的变量赋值操作后面会有一个内存屏障,
* 读操作不会被重排序到内存屏障之前。
*/
public class Singleton(){
// 将构造函数私有化
private Singleton(){}
private volatile static Singleton singleton = null;
// 提供能够访问的静态方法
public static Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
// 注意,这句话并不是原子的
singleton = new Singleton();
}
}
}
return singleton;
}
}
第二个版本
/**
* 2.1 一个简单的版本
* jdk1.5
* 这种方法最大的问题是:类被加载的时候,new Singleton() 就被执行了。
* 我们的构造函数可能依赖于其他的类干一些事情,希望当我们第一次调用getInstance()
* 的时候才被真正创建。这个时候就不能把对象的创建委托为类装载器了。
*/
public class Singleton(){
private Singleton(){}
private volatile static Singleton singleton = new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
第二个版本,改进
/**
* 2.2 不依赖与jdk的版本
* 只有在调用 getInstance() 的时候, 才会把实例创建出来
*/
public class Singleton(){
private Singleton(){}
private static class SingletonHolder(){
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
第三个版本
/**
* 3.1 优雅的版本
* 使用枚举
* 默认枚举实例创建是线程安全的,但枚举里面的其他任何方法的线程安全是由程序员控制。
*/
public enum Singleton(){
INSTANCE;
}