Javaee:单例模式
文章目录
- 单例模式
- 单例模式的使用场景
- 单例模式的实现方式
- 饿汉模式(急)
- 实现方式
- 懒汉模式(缓)
- 使用静态内部类创建单例模式(推荐)
- 总结
单例模式
保证一个类只能创建一个实例,不能创建多个实例
单例模式的使用场景
如果频繁的创建和销毁对象的开销非常大,并且这些类的对象可以复用,势必会造成资源浪费,性能低和资源利用率低
举例:数据库连接池
访问数据库,需要创建数据库链接对象,创建数据库链接对象和销毁的开销是非常大的,而数据库连接池会采用单例模式,只实例一个对象来确保连接池的唯一性和全局可访问性,也就很好的提高资源利用率
好处
- 通过重用连接池中的连接,避免了频繁地创建和销毁数据库连接,从而节省了系统资源。
- 可以更容易地管理和监控连接池的状态和性能。
线程池中也应用了单例模式的设计模式
单例模式的实现方式
饿汉模式(急)
在类加载的同时创建实例
实现方式
class Singlation{
private static Singlation instance=new Singlation();
public static Singlation getInstance() {
return instance;
}
private Singlation(){}
}
由于在类加载的过程就创建了实例,不涉及线程安全问题
重点:
- 使用private修饰构造方法——不能通过new直接创建对象
- 使用static修饰对象——类加载就创建好对象
- 通过调用getInstance——获得实例对象
优点
类加载就创建实例,只涉及到读操作,天生线程安全 |
---|
没有加锁,可以直接使用,减少延迟 |
缺点
无论是否需要使用都会在类加载时创建,可能会导致资源浪费 |
---|
若是重启服务,无法实现延迟加载的特性,会拖慢运行速度 |
懒汉模式(缓)
只有在真正需要时才创建实例
单线程版本
class SingletionLazy {
private static SingletionLazy instance = null;
public static SingletionLazy getInstance() {
if (instance == null) {
instance = new SingletionLazy()
}
return instance;
}
}
只有在真正需要时才创建实例——懒
举例:
妈妈叫我去小卖部买酱油,而我在看电视,此时我的做法是不听妈妈的话,继续看电视,等到妈妈要生气了我才去买
不难发现上述代码是线程不安全的,在多个线程下,很有可能会越过判断语句,直接去创建对象
改进
class SingletionLazy {
private static SingletionLazy instance = null;
public static SingletionLazy getInstance() {
synchronized (locker) {
if (instance == null) {
instance = new SingletionLazy();
}
}
return instance;
}
}
加锁,在构建对象的时候才需要考虑同步,所以我们对于new对象的操作进行加锁,上述代码中getInstance是读操作,不存在锁竞争,所有线程都能访问,而第二步的判断,如果没有实例创建,所有的线程就会去竞争锁,抢到锁的线程创建好实例,以后的getInstance操作都是直接返回当前实例对象。
但是有个问题,如果同时有两个线程进入了if语句,其中一个线程拿到锁创建实例之后将锁释放了,此时另一个线程也能创建实例——不符合要求,线程不安全
解决方案:双重检锁
使用if语句检查当前实例是否已经被创建
还要注意内存可见性问题
instance = new SingletionLazy();
对应三条指令
- 给对象分配内存
- 初始化对象
- 返回对象指向的内存地址
编译器在逻辑不改变的情况会自动优化,进行指令重排序
解决方案:volatile关键字
优化版本
class SingletionLazy {
private static volatile SingletionLazy instance = null;
private static Object locker = new Object();
public static SingletionLazy getInstance() {
if (instance == null) {//判断实例是否已经创建
synchronized (locker) {
if (instance == null) {//判断是否需要new对象
instance = new SingletionLazy();
}
}
}
return instance;
}
}
优点
真正需要创建实例的时候,先判断是否为空,如果为空,再创建单例对象 |
---|
真正需要用到的时候创建减少了不必要的开销 |
使用静态内部类创建单例模式(推荐)
这种方式是线程安全的,不需要额外的同步(不用加锁)
public class Singleton {
// 私有构造函数
private Singleton() {
// 初始化代码
}
// 静态内部类,负责持有单例实例
private static class SingletonHelper {
private static final Singleton INSTANCE = new Singleton();
}
// 获取单例实例的公共静态方法
public static Singleton getInstance() {
return SingletonHelper.INSTANCE;
}
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
}
}
总结
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。有多种实现方式,懒汉模式需要注意线程安全问题。