单例设计模式提供了多线程情况下保证实例唯一性的解决方案。
评估方式:线程安全、高性能、懒加载。
实现方式:饿汉模式、懒汉模式、Double-Check、Holder、枚举及以上各种模式的优化和扩展。
饿汉模式
1. 线程安全:在初始化的过程种会被收集进()方法里,该方法能百分百保证同步。
备注:
1. init is the (or one of the) constructor(s) for the instance, and non-static field initialization. 2. clinit are the static initialization blocks for the class, and static field initialization.
init是instance实例构造器,对非静态变量解析初始化,
clinit是class类构造器对静态变量,静态代码块进行初始化
2. 性能:可能造成内存浪费,在被ClassLoader加载后,可能会很长一段时间才被使用,意味着instance会长时间存在于instance实例开辟的堆内存里,如果单例类的属性很多,会造成更大的浪费。getInstance没有加锁,多线程情况下性能比较高。
3. 无法进行懒加载。
/**
* 饿汉模式:指全局的单例实例在第一次被使用时构建
* 补充: final 不允许被继承
*/
public final class Singleton {
// 实例变量
private byte[] data = new byte[1024];
// 定义实例时直接初始化
private static Singleton instance = new Singleton();
// 定义私有的构造函数 -> 不允许外部new
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
懒汉模式
1. 线程不安全: 没有锁或其他保障,多线程情况下,可能生成多个实例。
2. 性能:初始化很快。
3. 可以进行懒加载。
/**
* 懒汉模式:指全局的单例实例在类装载时构建
* 补充: final 不允许被继承
*/
public final class Singleton {
// 实例变量
private byte[] data = new byte[1024];
// 定义实例但不初始化
private static Singleton instance;
// 定义私有的构造函数 -> 不允许外部new
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
懒汉模式 + synchronized
1. 线程安全
2. 性能:初始化很快,但获取实例在多线程情况下性能不高,因为synchronized关键字会使方法同一时刻只能被一个线程访问。
3. 可以进行懒加载。
/**
* 懒汉模式:指全局的单例实例在类装载时构建
* 补充: final 不允许被继承
*/
public final class Singleton {
// 实例变量
private byte[] data = new byte[1024];
// 定义实例但不初始化
private static Singleton instance;
// 定义私有的构造函数 -> 不允许外部new
private Singleton() {
}
// 加上同步控制,每次只允许一个线程进入
public static synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
Double-Check + Volatile
1. 线程安全:首次初始化的时候加锁,之后允许多个线程进行getInstance方法的调用来获取实例。
2. 性能:性能高,只在实例没被初始化的时候,加锁一次(即在可能发生竞争的模块加锁)。
3. 可以进行懒加载。
/**
* 补充: final 不允许被继承
*/
public final class Singleton {
// 实例变量
private byte[] data = new byte[1024];
// 定义实例但不初始化
// 如果instance先被实例化了,这两个未被实例化,会抛出空指针异常,volatile保证指令不被重排序
private volatile static Singleton instance;
//
Connection connection;
Socket socket;
// 定义私有的构造函数 -> 不允许外部new
private Singleton() {
this.connection;
this.socket;
}
public static Singleton getInstance() {
if (null == instance) {
// instance为null时才创建,避免每次都进入同步代码块
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
Holder
1. 线程安全:类似饿汉模式,实例化时候,被收集到()方法里了,该方法时同步方法,可以保证内存可见性,JVM指令的顺序行和原子性。
2. 性能:性能较好,在初始化的时候并不加载,在实例化的时候才新建实例。这种方法时单例设计最好的设计之一,也是目前使用比较广的设计之一。
3. 可以进行懒加载。
/**
* 补充: final 不允许被继承
*/
public final class Singleton {
// 实例变量
private byte[] data = new byte[1024];
// 定义私有的构造函数 -> 不允许外部new
private Singleton() {
}
// 在静态内部类持有Singleton实例,并且可被直接初始化
private static class Holder {
private static Singleton instance = new Singleton();
}
// 获取Holder的instance静态属性
public static Singleton getInstance() {
return Holder.instance;
}
}
枚举方式
1. 线程安全:枚举类型不允许被继承,且只能被实例化一次。
2. 性能:性能较好,在初始化的时候并不加载,在实例化的时候才新建实例。这种方法时单例设计最好的设计之一,也是目前使用比较广的设计之一。
3. 不能进行懒加载。
public enum Singleton {
INSTANCE;
// 实例变量
private byte[] data = new byte[1024];
Singleton() {
// 观察什么时候被执行
System.out.println("INSTANCE has be initialized.");
}
public static void method() {
// 调用该方法会主动使用Singleton,INSTANCE将会被实例化
}
public static Singleton getInstance() {
return INSTANCE;
}
}
枚举方式 + Holder
1. 线程安全:枚举类型不允许被继承,且只能被实例化一次。
2. 性能:性能较好,在初始化的时候并不加载,在实例化的时候才新建实例。这种方法时单例设计最好的设计之一,也是目前使用比较广的设计之一。
3. 可以进行懒加载。
public class Singleton {
// 实例变量
private byte[] data = new byte[1024];
// 定义私有的构造函数 -> 不允许外部new
private Singleton() {
}
private enum EnumHolder {
INSTANCE;
private Singleton instance;
EnumHolder() {
this.instance = new Singleton();
}
private Singleton getSingleton() {
return instance;
}
}
public static Singleton getInstance() {
return EnumHolder.INSTANCE.getSingleton(); }
}
面试相关:
云从科技、美团
参考:
1. 《Java高并发编程详解》 汪文君
2. 单例模式 - 维基百科 zh.wikipedia.org/wiki/%E5%8D…
3. init和clinit的区别 - StackOverFlow stackoverflow.com/questions/8…