设计模式-单例模式
设计模式-单例模式
官方定义
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。并且该类只提供一个取得其对象实例的方法(静态方法)
举个最常见的例子,Spring中bean的默认都是单例模式,每个bean定义只生成一个对象实例,每次getBean请求获得的都是此实例
单例模式八种方式
那接下来我们来聊聊单例模式的八种实现方式,如下所示
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举方式
饿汉式(静态常量)
//案例演示 - 饿汉式(静态常量)
class Singleton{
//一、构造器私有化,防止外部用构造器初始化。。。
private Singleton(){}
//二、类的内部创建对象 final static
private static final Singleton singleton = new Singleton();
//三、对外提供公共的静态方法,返回该类的唯一对象
public static Singleton getInstance(){
return singleton;
}
}
写法分析
优势:简单、避免多线程的同步问题
劣势:没有达到懒加载的效果
饿汉式(静态代码块)
class Singleton(){
//构造器私有化
private Singleton(){}
//类的内部创建对象
private static final Singleton singleton;
static {
singleton = new Singleton();
}
//对外提供公共的静态方法
public static Singleton getInstance(){
return singleton;
}
}
写法分析
优势:简单,避免了多线程同步问题
劣势:没有实现懒加载效果
懒汉式(线程不安全)
class Singleton(){
//构造器私有化
private Singleton(){}
//类的内部创建对象
private static Singleton singleton;
//对外提供公共的静态方法
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
写法分析
优势:起到了懒加载的效果,不会造成内存浪费
劣势:线程不安全,实际开发中不推荐这个方式
懒汉式(同步方法)
class Singleton(){
//构造器私有化
private Singleton(){}
//类的内部创建对象
private static Singleton singleton;
//对外提供公共的静态方法
public static synchronized Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
写法分析
解决了线程安全问题,但是效率太低
懒汉式(同步代码块)
class Singleton(){
//构造器私有化
private Singleton(){}
//类的内部创建对象
private static Singleton singleton;
//对外提供公共的静态方法
public static Singleton getInstance(){
if(singleton == null){
synchronized(Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
写法分析
不推荐,解决不了线程安全的问题
双重检查机制
class Singleton(){
//构造器私有化
private Singleton(){}
//类的内部创建对象
private static Singleton singleton;
//对外提供公共的静态方法
public static Singleton getInstance(){
if(singleton == null){
synchronized(Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
写法分析
在实际开发中推荐使用这种方式
- 线程安全
- 懒加载
- 效率很高
可能出现的问题
我们认为的new Singleton()操作
- 分配内存地址M
- 在内存M上初始化Singleton对象
- 将M的地址值赋值给instance对象
JVM编译优化后(指令重排)可能的new Singleton()操作
- 分配内存地址M
- 将M的地址赋值给instance变量
- 在内存M上初始化Singleton对象
会出现空指针异常
解决方法
只需要关键字volatile 禁止指令重排
private static volatile Singleton singleton;
扩展-Volatile
轻量级的同步机制(低配版)没有保证原子性
三大特性
- 保证可见性
- 其中一个线程修改了主内存共享变量的值,要写回主内存,并要及时通知其他线程可见
- 没有保证原子性
- (不能保证)不可以分割,完整,要么同时成功,要么同时失败
- 禁止指令重排
- 和底层内存屏障相关,避免多线程下出现指令乱序的情况
静态内部类方式
class Singleton{
private Singleton(){}
private static class SingLetonInstance{
public static final Singleton INSTANG = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.INSTANG;
}
}
写法分析
不会出现线程安全问题
JVM来帮我们保证线程的安全性‘
利用了静态内部类的特点,效率也很高,开发中推荐
枚举方式
enum Singleton{
INSTANCE;
}
写法分析
不仅可以避免线程安全问题,推荐大家使用
docker