单例模式
定义:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类中只提供一个取得其对象实例的方法(静态方法)
饿汉式
静态常量实现
这里使用了一个小知识,构造器私有化,防止外部new,通过这个技术可以实现在这个类的内部实例化对象;
在内部使用private static关键字对实例进行修饰,在类装载的过程中就对类进行实例化,通过这样的方式实现单例,避免线程同步问题,但无法起到懒加载的功能,可能造成资源浪费。
class Singleton {
//1. 构造器私有化, 防止外部 new
private Singleton() {
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() { return instance;
}
}
静态代码块
使用静态代码块的方式与使用静态变量的方式区别不大,类的实例化过程仍然在类的装载过程中实现,因此也是线程安全的。
//饿汉式(静态变量)
class Singleton {
//1. 构造器私有化, 外部不能 new
private Singleton() {
}
//2.本类内部创建对象实例
private static Singleton instance;
static {
// 在静态代码块中,创建单例对象
instance = new Singleton();
}
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() { return instance;
}
}
以上两种方案中,无论是静态变量还是静态代码块都是线程安全的饿汉式,除此之外,还有线程不安全的饿汉式,用文字来描述就是:
我们以静态变量的代码为例子,依旧使用
private static Singleton instance;
对实例进行修饰,但我们不在类初始化过程中对对象进行实例化,将这个过程延迟(也就是懒加载),在业务需要使用该实例的时候再对该实例进行初始化,具体代码如下:
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
根据以上代码,我们很容易发现问题,当并发度较高时,假如有两个线程同时判断instance为空,那么两个线程实例化一个类,造成多次实例化,这也就打破了单例模式,我们称这样子的情况为线程不安全的,那么如何实现线程安全的延迟加载呢,这就可以谈到我们的懒汉式单例模式
懒汉式
同步方法
简单来说,对getInstance()
的方法加一个Synchronized关键字,给方法上一个方法级的锁,这样是能保证单例的,但这样有很多的缺点,我其实只想保证我在一开始实例化过程中的线程安全,当类实例化后,只有一个实例,是否线程安全已经不重要了,但因为Synchronized关键字的存在,即使实例已经初始化,依旧需要阻塞,这是效率很差的单例模式,我们不考虑。
同步代码块
同步代码块基本与同步方法一致,基本等同于方法级,因此不推荐。
双重检查
这是推荐使用的,双重检查也是我们开发中经常会使用单例模式实现方案,其优点是:懒加载,线程安全,效率较高;
双重检查的代码中,实例对象使用了volatile关键字修饰的原因:
instance = new Singleton()
这段代码是分三步执行的:
- 为instance分配内存空间
- 初始化instance
- 将instance指向分配的内存地址
但是由于JVM具有指令重排的特性,执行顺序有可能会变成1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下导致一个线程获得还没有初始化的实例。例如:线程T1执行了1和3,此时T2调用getInstance()
发现instance不为空,因此返回实例,但此时实例还没初始化,使用volatile关键字可以禁止JVM的指令重排,保证在多线程环境下也能正常运行。
具体实现如下代码:
class Singleton {
private static volatile Singleton instance;
// 构造器私有化
private Singleton() {}
// 提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题,同时保证了效率,推荐使用
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
枚举
单例模式在JDK的应用
java.lang.Runtime
类是经典的单例模式应用者(饿汉式)