一、单例设计模式
1、单例设计模式的定义
单例设计模式:一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫做单例设计模式,简称单例模式
2、单例设计模式的用处
从业务概念上,有些数据在系统中只应该保存一份,就比较适合设计为单例类。比如,系统的配置信息类。
3、如何实现一个单例设计模式
- 构造函数需要是
private
访问权限的,这样才能避免外部通过new
创建实例 - 考虑对象创建时的线程安全问题
- 考虑是否支持延迟加载
- 考虑
getInstance()
性能是否高(是否加锁)
4、单例设计模式的经典实现方式
1)饿汉式
饿汉式的实现方式比较简单。在类加载的时候,instance
静态实例就已经创建并初始化好了,所以instance
实例的创建过程是线程安全的。不过,这样的实现方式不支持延迟加载。
/**
* todo 饿汉式(静态常量)
*/
public class Singleton {
private AtomicInteger id = new AtomicInteger(0);
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
public Integer getId(){
return id.incrementAndGet();
}
}
2)懒汉式
有饿汉式,对应的,就有懒汉式。懒汉式相对于饿汉式的优势是支持延迟加载。
/**
* todo 懒汉式(同步方法)
*/
public class Singleton {
private AtomicInteger id = new AtomicInteger(0);
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
public Integer getId(){
return id.incrementAndGet();
}
}
不过懒汉式的缺点也很明显,我们给getInstance()
这个方法加了一把大锁(synchronized
),导致这个函数的并发度很低。量化一下的话,并发度是1
,也就相当于串行操作了。而这个函数是在单例使用期间,一直会被调用。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶颈,这种方式是不可取的。
####3)双重检查 饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。再来看一种既支持延迟加载、又支持高并发的单例实现方式,也就是双重检查实现方式。
/**
* todo 懒汉式(双重检查)
*/
public class Singleton {
private AtomicInteger id = new AtomicInteger(0);
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;
}
public Integer getId(){
return id.incrementAndGet();
}
}
在这种实现方式中,只要instance
被创建之后,即便再调用getInstance()
函数也不会再进入到加锁逻辑中了。所以,这种实现方式解决了懒汉式并发度低的问题
4)静态内部类
我们再来看一种比双重检查更加简单的实现方法,那就是利用java的静态内部类。它有点类似饿汉式,但又能做到延迟加载。
/**
* todo 静态内部类
*/
public class Singleton {
private AtomicInteger id = new AtomicInteger(0);
private Singleton() {}
private static class SingletonInstance {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.instance;
}
public Integer getId(){
return id.incrementAndGet();
}
}
5)枚举
最后,我们介绍一种最简单的实现方式,基于枚举类型的单例实现。这种实现方式通过Java枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。
/**
* todo 枚举
*/
public enum Singleton {
INSTANCE;
private AtomicInteger id = new AtomicInteger(0);
public Integer getId(){
return id.incrementAndGet();
}
}