目录
四、多线程案例之--单例模式
3.1 单例模式
3.2 怎么去设计一个单例?
饿汉模式
懒汉模式
3.3 两种模式的总结
四、多线程案例之--单例模式
4.1 单例模式
是校招中最常考的设计模式之一.
啥是设计模式?
设计模式好比象棋中的 " 棋谱 ". 红方当头炮 , 黑方马来跳 . 针对红方的一些走法 , 黑方应招的时候有一些固定的套路. 按照套路来走局势就不会吃亏 .软件开发中也有很多常见的 " 问题场景 ". 针对这些问题场景 , 大佬们总结出了一些固定的套路 . 按照这个套路来实现代码, 也不会吃亏 .
单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例.
这一点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要一个.
(定义了数据库的用户名,密码,连接串,通过DataSource的实例对象获取数据库的连接)
4.2 怎么去设计一个单例?
1.口头约定
对外提供一个方法,要求大家使用这个对象的时候,通过这个方法来获取
(不靠谱,不采用)
2.使用编程语言本身的特性来处理
首先要分析清楚,在Java中哪些对象是全局唯一的
.class对象 比如String.class 类对象
饿汉模式
既然是单例那么通过new的方式去获取对象是有歧义的 那么就不能让外部去new这个对象
Singleton singleton = new Singleton();
//构造方法私有化
private Singleton(){}
public static Singleton getInstance(){
return instance;
}//用static修饰方法
private static Singleton instance = new Singleton();
//类似于这种类一加载就完成初始化的方式 称为饿汉模式
//书写简单不易出错
懒汉模式
避免程序启动的时候浪费过多的系统资源,当程序使用这个对象时再对它进行初始化
//定义一个类成员变量
private static SingletonLazy instance = null;
public static SingletonLazy getInstance(){
//在获取成员变量的时候判断是否已经初始创建
//如果没有则创建出来
if(instance == nul){
instance = new SingletonLazy();
}
return instance;
}
在多线程环境下获取单例对象:
// 创建多个线程,并获取单例对象
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
// 获取单例对象,并打印
SingletonLazy instance = SingletonLazy.getInstance();
System.out.println(instance);
});
// 启动线程
thread.start();
}
}
}
通过检查发现获取到了不同的对象,出现了线程不安全
分析造成线程不安全的原因:
为了解决这个问题,可以把初始化代码加锁:
public synchronized static SingletonLazy getInstance(){
可以看到依然出现了线程安全问题,那么就需要扩大锁的力度
输出正常、
但是还剩一个非常严重的问题!
举个栗子:
为了避免过度竞争消耗,加入一层判断
public static SingletonDCL getInstance() {
// 为了让后面的线程不再获取锁,避免了锁竞争造成的资源浪费
if (instance == null) {
synchronized (SingletonDCL.class) {
// 完成初始化操作,只执行一次
if (instance == null) {
instance = new SingletonDCL();
}
}
}
//双重检查锁
通过加synchnized关键字解决了原子性,内存可见性问题,那有序性如何保证?
那么就需要给共享变量加volatile关键字
public class SingletonDCL {
// 定义一个类成员变量
private volatile static SingletonDCL instance;
// 构造方法私有化
private SingletonDCL() {
}
public static SingletonDCL getInstance() {
// 为了让后面的线程不再获取锁,避免了锁竞争造成的资源浪费
if (instance == null) {
synchronized (SingletonDCL.class) {
// 完成初始化操作,只执行一次
if (instance == null) {
instance = new SingletonDCL();
}
}
}
// 返回实例对象
return instance;
}
}
4.3 两种模式的总结