单例模式五种实现方式以及在JDK中的体现
一、五种实现方式
1、饿汉式
- 构造私有
- 提供一个静态私有的成员常量,类型就是单例类型,值是用私有构造创造出来的唯一实例
- 提供公共的静态方法获取上述的静态成员常量
java
public class Singleton1 implements Serializable {
//(1)构造私有
private Singleton1(){
//添加if判断防止单例模式被破坏
if(INSTANCE != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
System.out.println("private Singleton1()");
}
//(2)提供一个静态私有的成员常量,类型就是单例类型,值是用私有构造创造出来的唯一实例
private static final Singleton1 INSTANCE = new Singleton1();
//(3)提供公共的静态方法获取上述的静态成员常量
public static Singleton1 getInstance() {
return INSTANCE;
}
}
注意:对应所有的单例模式实现方式,如果实现Serializable接口,可能会被反射,反序列化等操作破坏单例,
2、枚举饿汉式
- 枚举类,只定义一个变量
- 构造私有
- 提供公共的静态方法获取枚举常量
java
public enum Singleton2 {
//(1)枚举类,只定义一个变量
INSTANCE;
//(2)构造私有
private Singleton2(){
System.out.println("private Singleton3()");
}
//(3)获取枚举常量
private static Singleton2 getInstance(){
return INSTANCE;
}
}
3、懒汉式
- 构造私有
- 提供一个静态私有的成员常量,类型就是单例类型,值为空
- 当调用该方法时才new
- 提供公共的静态方法获取上述的静态成员常量
java
public class Singleton3 {
//(2)声明静态变量为空
private static Singleton3 INSTANCE = null;
//(1)构造私有
private Singleton3(){
System.out.println("private Singleton1()");
}
//(3)提供公共的静态方法获取上述的静态成员变量
//添加synchronized保证在多线程情况下运行正常
public static synchronized Singleton3 getInstance() {
//(4)调用该方法时再new,通过判断是否为空可控制只创建一个实例
if(INSTANCE == null) {
INSTANCE=new Singleton3();
}
return INSTANCE;
}
}
注意:如果在多线程下,要防止多次创建实例,则在静态方法上添加synchronized,但其实我们只要在第一次调用时保证线程安全就可以,因此此方式的效率比较低,如果要提高效率,可以参考下述两种实现方式。
4、DCL懒汉式-双检索懒汉式
DCL懒汉式是为了保证多线程下运行正常,同时提高效率,也就是在静态方法中加锁之前判断是否已经创建了实例。同时静态变量使用volatile修饰
java
public class Singleton4 {
//(2)声明静态变量为空
private static volatile Singleton4 INSTANCE = null;//可见性,有序性
//(1)构造私有
private Singleton4(){
System.out.println("private Singleton1()");
}
//(3)提供公共的静态方法获取上述的静态成员变量,调用该方法时再new
public static Singleton4 getInstance() {
//在加锁之前进行判断
if(INSTANCE == null) {//一次检索
synchronized (Singleton4.class){
if (INSTANCE == null){//二次检索
INSTANCE=new Singleton4();
}
}
}
return INSTANCE;
}
}
5、懒汉式内部类单例模式
使用内部类的方式可以保证线程安全,建议使用
java
public class Singleton5 {
//构造私有
private Singleton5(){
System.out.println("private Singleton5()");
}
//创建内部类,在内部类中新建实例
private static class Holder{
static Singleton5 INSTANCE=new Singleton5();
}
private static Singleton5 getInstance(){
return Holder.INSTANCE;
}
}
二、单例模式在jdk中的体现
- Runtime类是单例模式,饿汉式单例实现
- System类中的有一个Console类型的cons变量,采用DCL懒汉式方式实现
- Collections类中有大量单例模式实现,采用内部类懒汉式实现