如果饿了就吃,困了就睡,渴了就喝,人生就太无趣了
1.定义
1.1 概念
确保一个类只有一个实例,并提供一个全局访问点。简单来说:将构造器私有化,并在静态方法中初始化该类的静态变量。
1.2 类图
如图1,定义一个该类的私有静态变量uniqueInstance
,并将构造器定义为private
(在图中没有显示),通过静态方法getInstance()
为uniqueInstance
初始化并返回该实例。
2.单例模式各种类型。
2.1 经典类型
在使用时会被创建,但是在并发模式中会出现问题。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance==null){
uniqueInstance=new Singleton();
}
return uniqueInstance;
}
}
2.2 同步锁类型
将getInstance
变成同步方法。在方法声明中加入 synchronized
关键字。
synchronized:让每个线程进入该关键字修饰的方法前,要先等待其他线程离开后才能访问。
问题:这样每次访问getInstance()
都会需要同步,但实际上只要uniqueInstance
被设置好后就不需要同步,只是第一次访问需要同步。如果程序需要大量访问getInstance()
方法,就会大大降低性能。
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
2.3 急切类型
将uniqueInstance
变量初始化交给静态初始化器。JVM
将保证线程安全,因为JVM
在加载这个类时会马上创建一个唯一的单间实例,JVM
保证任何线程访问uniqueInstance
前,一定会先创建此实例。
public class Singleton {
private volatile static Singleton uniqueInstance=new Singleton();
private Singleton() {}
public static Singleton getInstance(){
return uniqueInstance;
}
}
2.4 双重检查加锁类型
使用双重检查加锁(double-checked locking),首先检查实例是否已经创建了,如果尚未创建,再进行同步。这样就只有第一次才会同步。
volatile:
- 类型修饰符。
- 保证了不同线程对这个变量进行操作时的可见性。例如果对变量值进行改变,对其他线程是立即可见。(实现可见性)
- 禁止进行指令重排序(实现有序性)
- 保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
这样就确保了uniqueInstance
被初始化时,多个线程能正确处理uniqueInstance
变量。
getInstance()
方法中的第一个if
保证在uniqueInstance
初始化后,避免同步。getInstance()
方法中的第二个if
保证在线程a,b同时进入后,能够有序执行同步锁中的内容。没有第二个if
也是不能保证单例的。
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
2.5 其他类型
没有搞定,也就不敢乱写。