【设计模式】Singleton Pattern 单例模式
1.单例模式
确保一个类只有一个实例,并提供一个全局访问点。
应用场景:对于我们只需要一个对象的场景,如线程池(threadpool)、缓存(cache),如果制造出多个实例,就会导致许多问题。
2.Singleton Pattern 类图
类变量持有唯一的单例实例,getInstance()方法是静态方法,可以直接使用Singleton.getInstance()获取实例,是全局访问点。
3.经典的单例模式实现方式
注意:经典单例模式是线程不安全的
代码:
/**
* 经典的单例模式:这是线程不安全的
* 注意:延迟实例化,即在我们需要这个实例的时候,才实例化
*/
public class Singleton {
//1.利用一个静态变量来记录Singleton类的唯一实例
private static Singleton uniqueInstance;
//这里可以有其他的有用的实例化变量
//2.把构造器声明为私有,只有Singleton类内才能调用构造器
private Singleton() {}
//3.使用静态方法getInstatnce()实例化对象,并返回这个实例
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
//这里是其他有用的方法
}
4.处理多线程场景下的单例模式
方法:将getInstance()方法同步成synchronized方法即可解决。
我们迫使每个线程进入这个方法之前,要先等候别的线程离开该方法,即不会有两个线程可以同时进入这个方法。
代码:
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
缺点:性能问题,只有在第一次创建实例的时候,才需要真正的同步,以保证唯一得到实例化变量,
一旦uniqueInstance设置OK之后,就不需要同步这个方法,之后每次同步造成性能下降。
5.Singleton Pattern 在多线程下提高性能的方法
a.如果getInstance()的性能对应用程序不是很关键,就什么不做。
b.使用“急切”创建实例,而不用延迟实例化的做法
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return uniqueInstance;
}
}
使用这种做法之后,对象会在JVM加载这个类时就创建,不管需不需要实例化的对象。
c.使用双重检查加锁,在getInstance()中减少使用同步
思路:首先检查实例是否创建,如果尚未创建,才在创建的部分进行同步,这样一来,只用在第一次创建是才进行同步。
补充:volatile关键字知识
不能保证操作的原子性,但是可以保证操作的是同一个变量的同一个内存。用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致的情况。volatile就是用来避免这种情况的。
volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)。
双重检查锁实现代码:
// 双重检查加锁不适用1.4及更早版本的Java
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次判断实例是否已经存在:
class Singleton{
private Singleton(){}
private static Object syncObj = new Object();
private static Singleton instance = null;
public static Singleton getInstance (){
if (instance == null){
lock (syncObj){
if (instance == null){
instance = new Singleton();
}
}
return instance;
}
}
}