1.什么是设计模式?
设计模式好比象棋中的“棋谱”,红方当头炮, 黑方马来跳。 针对红方的一些走法, 黑方应招的时候有一些固定的套路。
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
设计模式是一套被反复使用的,多数人知晓的,经过分类编目的,代码设计经验的总结。
2.设计模式的作用是什么?
使用设计模式就是为了重用代码,让代码更容易被他人理解,保证代码可靠性。
常见设计模式之单例模式
单例模式是一种经典的设计模式
定义:单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例。
实现方式: " 饿汉 " 和 " 懒汉 "
1、饿汉:比如你家里中午五个人吃完饭,可以选择<font color="#99FF66“>立即把这五个碗给刷了。
饿汉的单例模式,是比较着急的去创建实例的。
2、懒汉:还是中午五个人吃完饭,晚上只有一个人在家,那么就只刷一个碗即可(懒)。
懒汉的单例模式,核心思想是非必要的时候,不创建。
针对单例模式会使用Singleton这个类来实现,保证Singleton这个类只有一个实例。
饿汉模式
类加载的同时, 创建实例。饿汉先把食物准备好,当饿了的啥时候就吃饭。
class Singleton{
//唯一实例的本体
private static Singleton instance = new Singleton();
//获取到实例的方法
public static Singleton getInstance(){
return instance;
}
//禁止外部new实例
private Singleton(){
}
}
public class ThreadDemo15 {
public static void main(String[] args) {
//此时 s1 和 s2 是同一个对象!!
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//Singleton s3 = new Singleton();
}
}
- 此时如果我们想在创建一个 Singleton 实例时,发现会报错
唯一实例的本体
private static Singleton instance = new Singleton();
懒汉模式
类加载的时候不创建实例,第一次使用的时候才创建实例。懒汉当饿了的时候,才想着准备食物。
class SingletonLazy {
private static SingletonLazy instance = null;
private SingletonLazy() {}
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
这个懒汉模式就不是立即创建实例,只是在用的时候才会创建。我们想一下,懒汉模式包含了读,也包含了写,那它是否存在线程安全问题呢?
如果在多个线程中同时调通 getInstance 方法,可能就会创建出多个实例。
那么我们该如何保证线程安全呢?
加锁!!!
synchronized 闪亮登场
class SingletonLazy {
private static Singleton instance = null;
private SingletonLazy() {}
public synchronized static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
这里加锁之后,当一个线程进入 getInstance 方法中,其他线程就会进入阻塞状态!
我们继续在加锁的状态下进行改动:
- 双重 if
- 给 instance 加上 volatile
class SingletonLazy{
//加 volatile 比较靠谱
private static volatile SingletonLazy instance = null;
private SingletonLazy(){};//构造方法设置为private
public static SingletonLazy getInstance(){
if(instance == null) {
//判断是否要加锁,如果对象有了就不必加锁了,此时线程安全
synchronized (SingletonLazy.class) {
if (instance == null) {//是否要创建对象
instance = new SingletonLazy();
}
}
}
return instance;
}
}
在这里我们思考一下为什么要加双重 if 判定 和 volatile
我们知道加锁/解锁是开销比较大的,而懒汉模式线程不安全只是出现在首次创建实例的时候,后续使用就不用再进行加锁了,这样开销就会减小。
外层的 if 就是判断当下 instance 实例是否已经创建出来。
volatile 是要求每次进行读值。使用volatile关键字会强制将修改的值立即写入主存,避免“内存可见性”导致读取的 instance 出现偏差。
小结
饿汉模式:天然就是安全的,只是读操作。
懒汉模式:不安全的,有读也有写
1.加锁,把 if 和 new 变成原子操作
2.双重 if ,减少不必要的加锁操作
3.使用 volatile 禁止指令重排序,保证后续线程肯定拿到的是完整的对象
今天的学习就讲到这里了,我们下次再见!