单例模式算是最耳熟的设计模式之一了,本文就大致介绍一下单例模式相关的知识。
什么是单例模式
单例模式是指在应用程序中单例类的实例对象有且仅有一个。
单例模式有哪些优点
可以避免每次使用某个类的功能时都需要重写创建一个对象实例,但是需要注意多线程下的线程安全问题,这个功能也可以用静态方法代替。其次某些类由于特殊性在当前应用中就应该只有一个实例,例如配置信息类、一个应用的配置信息应该统一维护在一个实例中。再比如唯一递增 ID 号码生成器,如果程序中有两个对象,那就会存在生成重复 ID 的情况。
单例模式的代码实现方式
要实现一个单例模式,通常需要关注一下几点:
- 构造函数需要是
private
访问权限,这样才能避免外部通过new
关键字创建实例; - 考虑对象创建时的线程安全问题;
- 考虑是否需要支持延迟加载;
- 考虑
getInstance()
性能是否够高;
以下列举了常见的几种创建方式。
恶汉式
在类加载时就已经初始化好了,创建过程肯定是线程安全的,但是失去了延迟加载的特性。
public class IdGenerator {
private final AtomicLong id = new AtomicLong(0);
private static final IdGenerator INSTANCE = new IdGenerator();
private IdGenerator(){
}
public static IdGenerator getInstance(){
return INSTANCE;
}
public long getId(){
return id.incrementAndGet();
}
}
懒汉式(低并发度)
懒汉式支持了延迟加载,但是为了保证创建过程的线程安全在 getInstance()
方法上加了 synchronized
锁,这就导致并发读降低了。
public class IdGenerator {
private final AtomicLong id = new AtomicLong(0);
private static IdGenerator instance;
private IdGenerator() {
}
public static synchronized IdGenerator getInstance() {
if (instance == null) {
instance = new IdGenerator();
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
懒汉式(双重检验锁)
懒汉式的另外一种实现方式,仅仅将首次创建 instance
实例的操作加上了 synchornized
锁,保证了延时加载的同事大大提升了 getInstance()
方法的并发度。
public class IdGenerator {
private final AtomicLong id = new AtomicLong(0);
//注意这里的实例变量需要通过「valatile」修饰,禁止指令重排序
private static volatile IdGenerator instance;
private IdGenerator() {
}
public static IdGenerator getInstance() {
if (instance == null) {
//注意这里加的是类级别的锁
synchronized (IdGenerator.class){
if (instance == null){
instance = new IdGenerator();
}
}
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
静态内部类实现
这种实现方式有点像饿汗式,但是却支持延迟加载。SingletonHolder
是一个静态内部类,在外部类加载的时候并不会加载内部类。只有在调用 getInstance()
静态方法时才会触发内部类 SingletonHolder
的加载,同时创建 instance
实例。instance
的唯一性,创建过程的线程安全性,都由 JVM 来保证,所以静态内部类的实现方式既支持延迟加载,又能保证创建过程的线程安全性。
public class IdGenerator {
private final AtomicLong id = new AtomicLong(0);
private IdGenerator() {
}
static class IdGeneratorHolder{
private static final IdGenerator INSTANCE = new IdGenerator();
}
public static IdGenerator getInstance() {
return IdGeneratorHolder.INSTANCE;
}
public long getId() {
return id.incrementAndGet();
}
}
枚举实现
枚举实现方式是最简单、最实用的一种实现方式。通过 java 枚举本身的特性保证了实例创建过程的线程安全性和实例的唯一性。
public enum IdGenerator {
INSTANCE;
private final AtomicLong id = new AtomicLong(0);
public long getId() {
return id.incrementAndGet();
}
}