1 单例模式
单例模式(Singleton Pattern)是Java的最简单的设计模式之一,它属于创建型模式,它保证了一个类只有一个实例,故而由该类自己负责创建这个实例并且保证该类的实例唯一。
1.1 单例模式的特点
1 单例类只有一个实例
2 单例类必须自己创建这个实例,负责维护这个实例的唯一
3 单例类必须给所有其他对象提供这个实例
2 单例模式的实现
2.1 单例类的实现
1 提供静态实例 + 私有构造保证唯一
2 提供静态方法,用以引用或者创建
2.2 单例类的视线方式
1 懒汉式
懒汉式又分为两种:线程安全和线程不安全
1.1 懒汉式-线程不安全
public class Singleton {
//静态实例
public static Singleton instance;
//私有构造
private Singleton(){}
//获得实例
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
1.2 懒汉式-线程安全,使用了synchronized 关键字使得线程安全,但是由此也会使得别的线程必须等待该线程方法调用完毕之后才能再调用该方法
public class Singleton {
//静态实例
public static Singleton instance;
//私有构造
private Singleton(){}
//获得实例
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
1.3 测试:
懒汉模式的两种方式代码基本相同,唯一的区别只是线程安全的懒汉模式使用了 synchronized 关键字。
public static void main(String[] args) {
Singleton01 instance = Singleton01.getInstance();
System.out.println(instance);
Singleton01 instance2 = Singleton01.getInstance();
System.out.println(instance2);
}
///////////////////////////////////////////////////
// Singleton01@1b6d3586
// Singleton01@1b6d3586
2 饿汉式
饿汉式虽然没有使用 synchronized 关键字但是依旧是线程安全的,因为 instance 在类加载的时候就直接实例化了,但正是由于这个原因,即使在后期没有使用到这个实例依旧会实例化该对象,造成内存空间的浪费
public class Singleton02 {
//静态实例 在类加载的时候先实例化
public static Singleton02 instance = new Singleton02();
//私有构造
private Singleton02(){}
//获得实例
public static Singleton02 getInstance(){
return instance;
}
}
3 双重锁/双重校验锁
采用了双锁机制,进行双重保证,在一个线程在创建实例的时候其他线程必须等待,这样保证了线程安全,避免重复创建实例
public class Singleton03 {
//静态实例 volatile 防止编译器进行优化
public volatile static Singleton03 instance;
//私有构造
private Singleton03(){}
//获得实例
public static Singleton03 getInstance(){
if(instance == null){
synchronized (Singleton03.class){
if(instance == null){
instance = new Singleton03();
}
}
}
return instance;
}
}
4 登记式/静态内部类
对静态域实现延迟初始化,这个方式的话,虽然Singleton04被加载了,但是Singleton04并没有被实例化,并且只有调用getInstance()方法的时候才会实例化对象
public class Singleton04 {
private static class Singleton04Holder{
private static final Singleton04 instance = new Singleton04();
}
private Singleton04(){}
private static final Singleton04 getInstance(){
return Singleton04Holder.instance;
}
}
5 枚举
是实现单例模式的最佳方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化【这个方法真心不理解为什么,大家了解一下】
public enum Singleton05 {
INSTANCE;
public void whateverMethod(){}
}
3 总结
一般情况下,不建议使用第 1 个两种懒汉方式,建议使用第 2种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 4 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 5种枚举方式。如果有其他特殊的需求,可以考虑使用第 3种双检锁方式。