✨哈喽,进来的小伙伴们,你们好耶!✨
🛰️🛰️系列专栏:【JavaEE】
✈️✈️本篇内容:设计模式之单例模式。
🚀🚀代码存放仓库gitee:JavaEE初阶代码存放!
⛵⛵作者简介:一名双非本科大三在读的科班Java编程小白,道阻且长,星夜启程!
1.什么是设计模式?
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的实验和错误总结出来的。
设计模式是一套被反复使用的,多数人知晓的,经过分类编目的,代码设计经验的总结。
2.设计模式的作用是什么?
使用设计模式就是为了重用代码,让代码更容易被他人理解,保证代码可靠性。
一、常见设计模式之单例模式
定义:单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例。
1、饿汉:比如你家里中午五个人吃完饭,可以选择立即把这五个碗给刷了(急)。
2、懒汉:还是中午五个人吃完饭,晚上只有两个人在家,那么就只刷两个碗即可(懒)。
饿汉的单例模式,是比较着急的去进行创建实例的。
懒汉的单例模式,是不太着急的去创建实例,只是在用的时候,才真正创建。
那么针对单例模式会使用Singleton这个类来实现,保证SIngleton这个类只有一个实例。
1、饿汉模式
代码演示:
class Singleton {
/**
* 单例模式-饿汉模式
*/
//1、使用static创建一个实例,并且立即进行实例化。
//这个instance 对应的实例,就是该类的唯一实例。
private static Singleton instance = new Singleton();
//2、为了防止程序员不小心在其他地方在此new 这个Singleton,所以给一个private 的构造方法。
private Singleton(){}
//3、提供一个方法,让外面能够唯一拿到实例。
public static Singleton getInstance(){
return instance;
}
}
public class demo19 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
}
}
1、假如我们想再创建一个SIngleton实例的时候,我们会发现idea会报错。
2、我们发现在类加载阶段就会创建实例,针对这个唯一实例的初始化比较着急!
private static Singleton instance = new Singleton();
2、懒汉模式
代码:
class singleton2 {
// 1.就不是立即就初始化实例
private static Singleton2 instance = null;// 2.把构造方法设为 private
private singleton2() {}
// 3.提供一个方法来获取到上述单例的实例只有当真正需要用到这个 实例 的时候,才会真正去创建这个实例public static Singleton2 getInstance() {if (instance == nul1) {
instance = new singleton2();
return instance;
}
}
那么这个懒汉模式就不是立即创建实例,只有在用的时候才会创建,那么由此,我们可以联想到一些问题,这个懒汉模式中即包含了读,又包含了写,那么是否存在线程安全问题呢?
线程安全不安全,具体指的是多线程环境下,并发的调用 getlnstance 方法,是否可能存在bug!
就比如这里:有两个线程t1,t2按照上述代码执行的话可能会new两个实例。
那么如何保证线程安全呢?
答案:加锁!!
那么谈到加锁我们首先想到的就是synchronized关键字。注意这里的关键字位置一定要加对了,加错位置也不能起到加锁效果!
1、加synchronized的地方要正确。
2、使用这里的类对象作为锁对象 类对象在程序中只有唯一一份。
3、就能保证多个线程调用getInstance的时候。
4、都是针对同一个对象进行的。
但是对于刚才这个懒汉模式的代码来说,线程安全不安全,是发生在 instance 被初始化之前的.未初始化的时候,多线程调用 getInstance就可能同时涉及到读和修改.但是一旦 instance 被初始化之后(一定不是 null, if条件一定不成立了),getlnstance 操作就只剩下两个读操作。
而按照上述的加锁方式 无论代码是初始化之后,还是初始化之前,每次调用 getnstance 都会进行加锁,也就意味着即使是初始化之后,已经线程安全了,但是仍然存在大量锁竞争。
解决方案:
让 getlnstance,初始化之前,才进行加锁,初始化之后,就不再加锁了。
在加锁这里再加上一层条件判定即可.条件就是当前是否已经初始化完成——(instance == nul)。
注意:上面的条件判定的是是否要加锁,下面的条件判定的是是否要创建实例。
那么到这里代码中还存在一个问题,什么问题呢?
就是假如现在有很多的线程都来调用这个getInstance方法,就会造成大量的从内存读instance操作,可能会造成让编译器把这个读内存操作优化成都编译器操作!
一旦这里触发了优化,后续如果第一个线程已经完成了针对 instance 的修改那么紧接着后面的线程都感知不到这个修改,仍然把 instance 当成 null。
解决方案:给instance加上volatile关键字即可,volatile关键字可保证内存可见性。
private static volatile Singleton2 instance = null;
完整懒汉模式代码:
class Singleton2{
/**
* 单例模式-懒汉模式
*/
private static volatile Singleton2 instance = null;
private Singleton2(){}
public static Singleton2 getInstance(){
if(instance == null){
synchronized (Singleton2.class){
if(instance == null){
instance = new Singleton2();
}
}
}
return instance;
}
}
public class demo20 {
public static void main(String[] args) {
Singleton2 instance = Singleton2.getInstance();
}
}
懒汉模式总结:
1、要在正确的位置加锁!
2、理解双重if判定的缘由!
3、使用volatile关键字!
OK,那么今天的学习案例就讲到这里啦,我们下期再见,886!!