什么是单例模式
Singleton:保证一个类只有一个实例,且提供一个访问它的全局访问点
为什么需要单例模式
- 有一些类只需要使用一个实例,如工具箱等
- 需要实现对唯一实例的受控访问
怎么实现单例模式
要求:
- 判断对象是否被实例化,若实例化了则直接使用实例
- 是否实例化应该由该类自己判断,不需要客户判断,不允许其他类实例该类,则需要构造方法私有化
- 客户通过该类的public方法使用实例
代码实现
单线程下
//懒汉式——需要时再创建(第一次使用时实例化)
public class Singleton{
private static Singleton instance;
private Singleton{};
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
//饿汉式——一开始就创建好(初始化时创建实例)
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton{};
public static Singleton getInstance(){
return instance;
}
}
考虑多线程
懒汉式:考虑两个线程同时判断instance是否为空,两个判断全部为真,创建两个实例,线程不安全
饿汉式:一开始就创建好,后续部分不进行创建,线程安全
比较项目 | 懒汉式 | 饿汉式 |
---|---|---|
含义 | 第一次使用时创建 | 类初始化时创建 |
优势与劣势 | 当不需要时不创建,节约系统资源,线程不安全 | 线程安全,浪费系统资源 |
多线程情况 | 不安全 | 安全 |
如何改进懒汉式和饿汉式
改进懒汉式
//在第一次使用时创建,且针对实例是否为空加锁 public class Singleton{ private static Singleton instance; private Singleton{}; //效果同直接在getInstance方法上加synchronized标签相同 public static Singleton getInstance(){ synchronized{ if(instance == null){ instance = new Singleton(); } } return instance; } } //缺点——当一个线程进入该方法后,其他线程都需要等待,性能上有损耗
继续改进
//双重校验锁——只有当实例为空的时候才进入同步代码块 public class Singleton{ private static Singleton instance; private Singleton{}; public static Singleton getInstance(){ if(instance == null){ synchronized(this){ if(instance == null){ instance = new Singleton(); } } } } } //为什么要判断instance两次: //AB线程同时判断instance为空,A进入同步代码块,获得实例,此时instance为具体引用,B进入同步代码块,若不判断instance是否为空,则会重新创建一个新的实例。
- 改进饿汉式
//使用内部类
public class Singleton{
private class InnerSingleton{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return InnerSingleton.instance;
}
}
//使用枚举类
public Enum Singleton{
INSTANCE;
public void otherMethod(){};
}
//使用方法
public static void Main(String[] args){
Singleton.INSTANCE.otherMethod();
}