多线程案例(1)--单例模式
作者:小教学发布时间:2023-09-19分类:程序开发学习浏览:79
目录
单例模式
饿汉模式
懒汉模式:
前言
多线程中有许多非常经典的设计模式(这就类似于围棋的棋谱),这是用来解决我们在开发中遇到很多“经典场景”,简单来说,设计模式就是一份模板,可以套用.
单例模式
顾名思义,就是一个程序只能含有一个实例,有的场景中,希望一个类只能有一个对象,例如jdbc中的数据源实例就只需要一个。虽说程序员可以在编写代码时只给类创建一个对象,但是人毕竟没有机器靠谱,所以大佬们就设计了一套模板,按照模板来写代码,就不会出大的差错.
实现单例模式的方法有两种:饿汉模式和懒汉模式.
饿汉模式
就是在类加载时,就创建实例.
class SingleDemo{
//在类加载的时候创建
private static SingleDemo instance = new SingleDemo();
//设置为private是为了防止在其他类中 new 一个实例,这样就不是单例模式了
private SingleDemo(){ }
//既然不能在外面创建实例,我们就要提供一个方法来得到这个唯一的实例
public static SingleDemo getInstance() {
return instance;//读取操作
}
}
懒汉模式:
顾名思义,就是在用到实例的时候再创建实例,与饿汉模式相比,提高了代码的效率.
class SingleLazyDemo{
private static SingleLazyDemo instance = null;
//设置为private是为了防止在其他类中new一个实例
private SingleLazyDemo(){ }
public static SingleLazyDemo getSingleLazyDemo(){
//第一次需要实例时,创建实例
if(instance == null){
//修改操作
instance = new SingleLazyDemo();
}
return instance;//读取操作
}
}
了解了什么是饿汉模式和懒汉模式,我有一个问题:上述两种写法,那种是线程安全的?
我在前几篇博客中提到过,如果多个线程,同时修改同一个变量,此时就可能出现线程安全问题,所以显而易见,饿汉模式是线程安全的,它的方法中只涉及到读取操作,而懒汉模式是线程不安全的、它的方法中涉及到读取和修改操作.画个图理解一下:
我是接下来,我们就来解决懒汉模式的线程安全问题。导致该模式出现线程安全的原因其实在图中已经体现出来了,这个一个非原子操作,针对这一问题,我们的解决方法就是加锁.
class SingleLazyDemo{
private static SingleLazyDemo instance = null;
//设置为private是为了防止在其他类中new一个实例
private SingleLazyDemo(){ }
public static SingleLazyDemo getSingleLazyDemo(){
synchronized (SingleLazyDemo.class){//使修改操作变成原子操作
if(instance == null){//第一次需要实例时,创建实例
instance = new SingleLazyDemo();
}
}
return instance;
}
}
此时,虽然懒汉模式的线程安全问题基本得到了解决,但是一旦这么写,后续每次调用获取实例,都需要先加锁,而加锁的开销是很大的,只要涉及加锁,那么该代码就基本与“高性能”无缘了。实际上,我们的实例化对象的操作(即修改操作)只是出现在第一次调用获取实例的时候。
一旦对象被新出来了,后续的线程调用获取实例就没有必要加锁了,因为这时候只用读取操作,线程是安全的,所以我们还需要再添加一个条件:
class SingleLazyDemo{
private static SingleLazyDemo instance = null;
//设置为private是为了防止在其他类中new一个实例
private SingleLazyDemo(){ }
public static SingleLazyDemo getSingleLazyDemo(){
if(instance == null){//判断是否线程安全,要不要加锁
synchronized (SingleLazyDemo.class){//使修改操作变成原子操作
if(instance == null){//判断是否实例化
instance = new SingleLazyDemo();
}
}
}
return instance;
}
}
如果虽然都是判断实例为空,则为是否为;如果为但是第一个,则为实际上是借此判断线程是否要加锁,如果为;如果为就说明需要执行修改操作,线程不安全,要加锁,如果不为,则为空;如果为说明线程只要执行读取操作,线程安全,不要加锁,则为说明线程只要执行读取操作,线程安全,不要加锁。而第二个If则是借此判断是否要实例化对象。
在经过上述修改后,此代码还有一个问题,这就涉及到了之前没细讲的指令重排序问题,该问题也是因为编译器优化导致的,编译器为了提高执行效率,可能会在逻辑顺序不变的情况下,调整原有代码的执行顺序.
比如:新操作,可以分成三步:1.申请内存空间:2.在内存空间上构造对象获得3分。把内存的地址赋值给实例引用。在单线程中,新操作可以按照1 2 3执行,也可以按照1 3 2执行,但是在多线程中,1 3 2这样执行就可能导致线程安全问题.
举个例子:当T1线程执行完1 3时、实例就已经是非空了,这个时候2还没有执行、T2线程就开始执行,因为这个时候实例非空,所以T2线程直接返回实例、这个时候如果T2线程中的代码访问实例中的属性和方法,那么就会出现错误、因为实例还没有构造对象。
这个问题就需要使用易失性关键字来修饰实例、这样就可以保证实例在新的过程中不会出现指令重排序的现象,下面是最终的代码:
class SingleLazyDemo{
private static volatile SingleLazyDemo instance = null;
//设置为private是为了防止在其他类中new一个实例
private SingleLazyDemo(){ }
public static SingleLazyDemo getSingleLazyDemo(){
if(instance == null){//判断是否加锁
synchronized (SingleLazyDemo.class){//使修改操作变成原子操作
if(instance == null){//判断是否实例化
instance = new SingleLazyDemo();
}
}
}
return instance;
}
}
- 上一篇:前端--Html
- 下一篇:在WordPress主题博客主题下载上穿梭
- 程序开发学习排行
-
- 1鸿蒙HarmonyOS:Web组件网页白屏检测
- 2HTTPS协议是安全传输,为啥还要再加密?
- 3HarmonyOS鸿蒙应用开发——数据持久化Preferences
- 4记解决MaterialButton背景颜色与设置值不同
- 5鸿蒙HarmonyOS实战-ArkUI组件(RelativeContainer)
- 6鸿蒙HarmonyOS实战-ArkUI组件(Stack)
- 7[Android][NDK][Cmake]一文搞懂Android项目中的Cmake
- 8鸿蒙HarmonyOS实战-ArkUI组件(mediaquery)
- 9Android广播如何解决Sending non-protected broadcast问题
- 最近发表
-
- WooCommerce最好的WordPress常用插件下载博客插件模块的相关产品
- 羊驼机器人最好的WordPress常用插件下载博客插件模块
- IP信息记录器最好的WordPress常用插件下载博客插件模块
- Linkly for WooCommerce最好的WordPress常用插件下载博客插件模块
- 元素聚合器Forms最好的WordPress常用插件下载博客插件模块
- Promaker Chat 最好的WordPress通用插件下载 博客插件模块
- 自动更新发布日期最好的WordPress常用插件下载博客插件模块
- WordPress官方最好的获取回复WordPress常用插件下载博客插件模块
- Img to rss最好的wordpress常用插件下载博客插件模块
- WPMozo为Elementor最好的WordPress常用插件下载博客插件模块添加精简版