JAVA多线程(5)

JAVA多线程(5)

线程安全问题概述

卖票问题分析

  • 单窗口卖票

img

一个窗口(单线程)卖100张票没有问题
单线程程序是不会出现线程安全问题的

  • 多个窗口卖不同的票

img

3个窗口一起卖票,卖的票不同,也不会出现问题
多线程程序,没有访问共享数据,不会产生问题

  • 多个窗口卖相同的票

img

3个窗口卖的票是一样的,就会出现安全问题
多线程访问了共享的数据,会产生线程安全问题

线程安全问题代码实现

模拟卖票案例

创建3个线程,同时开启,对共享的票进行出售

public class Demo01Ticket {public static void main(String[] args) {//创建Runnable接口的实现类对象RunnableImpl run = new RunnableImpl();//创建Thread类对象,构造方法中传递Runnable接口的实现类对象Thread t0 = new Thread(run);Thread t1 = new Thread(run);Thread t2 = new Thread(run);//调用start方法开启多线程t0.start();t1.start();t2.start();}
}
public class RunnableImpl implements Runnable{//定义一个多个线程共享的票源private int ticket = 100;//设置线程任务:卖票@Overridepublic void run() {//使用死循环,让卖票操作重复执行while(true){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}
}

线程安全问题原理分析

img

线程安全问题产生原理图

分析:线程安全问题正常是不允许产生的,我们可以让一个线程在访问共享数据的时候,无论是否失去了cpu的执行权;让其他的线程只能等待,等待当前线程卖完票,其他线程在进行卖票。

解决线程安全问题办法1-synchronized同步代码块

同步代码块:synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

使用synchronized同步代码块格式:

synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}

代码实现如下:

public class Demo01Ticket {public static void main(String[] args) {//创建Runnable接口的实现类对象RunnableImpl run = new RunnableImpl();//创建Thread类对象,构造方法中传递Runnable接口的实现类对象Thread t0 = new Thread(run);Thread t1 = new Thread(run);Thread t2 = new Thread(run);//调用start方法开启多线程t0.start();t1.start();t2.start();}
}
public class RunnableImpl implements Runnable{//定义一个多个线程共享的票源private int ticket = 100;//创建一个锁对象Object obj = new Object();//设置线程任务:卖票@Overridepublic void run() {//使用死循环,让卖票操作重复执行while(true){//同步代码块synchronized (obj){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}}
}

⚠️注意:

  1. 代码块中的锁对象,可以使用任意的对象。
  2. 但是必须保证多个线程使用的锁对象是同一个。
  3. 锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行。

同步技术原理分析

同步技术原理:

使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁,也叫对象监视器

3个线程一起抢夺cpu的执行权,谁抢到了谁执行run方法进行卖票。

  • t0抢到了cpu的执行权,执行run方法,遇到synchronized代码块这时t0会检查synchronized代码块是否有锁对象

发现有,就会获取到锁对象,进入到同步中执行

  • t1抢到了cpu的执行权,执行run方法,遇到synchronized代码块这时t1会检查synchronized代码块是否有锁对象

发现没有,t1就会进入到阻塞状态,会一直等待t0线程归还锁对象,t0线程执行完同步中的代码,会把锁对象归 还给同步代码块t1才能获取到锁对象进入到同步中执行

总结:同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步。

解决线程安全问题办法2-synchronized普通同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

格式:

public synchronized void payTicket(){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}

代码实现:

 public /**synchronized*/ void payTicket(){synchronized (this){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}

分析:

定义一个同步方法,同步方法也会把方法内部的代码锁住,只让一个线程执行。

同步方法的锁对象是谁?

就是实现类对象 new RunnableImpl(),也是就是this,所以同步方法是锁定的this对象。

解决线程安全问题办法3-synchronized静态同步方法

同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
格式:

public static synchronized void payTicket(){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}

代码实现:

 public static /**synchronized*/ void payTicketStatic(){synchronized (RunnableImpl.class){//先判断票是否存在if(ticket>0){//提高安全问题出现的概率,让程序睡眠try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");ticket--;}}}

分析:

静态的同步方法锁对象是谁?

不能是this,this是创建对象之后产生的,静态方法优先于对象

静态方法的锁对象是本类的class属性–>class文件对象(反射)。

解决线程安全问题办法4-Lock锁

Lock接口中的方法:

  • public void lock() :加同步锁。
  • public void unlock() :释放同步锁

使用步骤:

  1. 在成员位置创建一个ReentrantLock对象
  2. 在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
  3. 在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

代码实现:

public class RunnableImpl implements Runnable{//定义一个多个线程共享的票源private int ticket = 100;//1.在成员位置创建一个ReentrantLock对象Lock l = new ReentrantLock();//设置线程任务:卖票@Overridepublic void run() {//使用死循环,让卖票操作重复执行while(true){//2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁l.lock();try {//先判断票是否存在if(ticket>0) {//提高安全问题出现的概率,让程序睡眠Thread.sleep(10);//票存在,卖票 ticket--System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "张票");ticket--;}} catch (InterruptedException e) {e.printStackTrace();}finally {l.unlock();//3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁//无论程序是否异常,都会把锁释放}}}

分析

java.util.concurrent.locks.Lock接口

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:

1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。

2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。

公平锁、非公平锁的创建方式:

//创建一个非公平锁,默认是非公平锁
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);//创建一个公平锁,构造传参true
Lock lock = new ReentrantLock(true);

3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定多个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

ReentrantLock和Synchronized的区别

相同点:

  1. 它们都是加锁方式同步;
  2. 都是重入锁;
  3. 阻塞式的同步;也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善);

不同点:

ck对象可以同时绑定多个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

ReentrantLock和Synchronized的区别

相同点:

  1. 它们都是加锁方式同步;
  2. 都是重入锁;
  3. 阻塞式的同步;也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善);

不同点:

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/198716.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UI 自动化测试框架设计与 PageObject 改造!

在 UI 自动化测试过程中,面对复杂的业务场景,经常会遇到这样的挑战: 简单的录制/回放速度快,但无法适应复杂场景;编写自动化测试脚本比较灵活,但工作量大且可维护性差;以往的封装技术&#xff…

【实用技巧】更改ArduinoIDE默认库文件位置,解放系统盘,将Arduino15中的库文件移动到其他磁盘

本文主要介绍更改Arduino IDE (含2.0以上版本)默认库文件位置的方法。 原创文章,转载请注明出处: 【实用技巧】更改ArduinoIDE默认库文件位置,解放C盘,将Arduino15中的库文件移动到其他磁盘-CSDN博客文章浏…

SpringBoot-过滤器Filter+JWT令牌实现登录验证

登录校验-Filter 分析 过滤器Filter的快速入门以及使用细节我们已经介绍完了,接下来最后一步,我们需要使用过滤器Filter来完成案例当中的登录校验功能。 我们先来回顾下前面分析过的登录校验的基本流程: 要进入到后台管理系统,我…

CleanMyMac X2024免费测试版好不好用?值不值得下载

如果你是一位Mac用户,你可能会遇到一些问题,比如Mac运行缓慢、磁盘空间不足、应用程序难以管理等。这些问题会影响你的Mac的性能和体验,让你感到沮丧和无奈。那么,有没有一款软件可以帮助你解决这些问题呢?答案是肯定的…

VR智慧景区:VR赋能文旅产业,激活消费潜能

随着国家数字化战略的不断深入实施,文旅产业数字化转型的步伐也在逐渐加快,以VR技术赋能文旅产业,让文旅景区线上线下双渠道融合,进一步呈现文化底蕴、激活消费潜能。 VR智慧景区以沉浸式、互动式、科技感的方式,将景区…

buildadmin+tp8表格操作(7.1)表格的事件监听(el-table中的事件)

因为buildAdmin是封装的 el-table的组件,所以el-table中的事件, 也是可以使用的, 两者有几个事件是有共同的(比如 双击事件), 这时可以根据自己的需要自行选择 以下代码是 buildadmin 使用 el-table中的事…

【Linux】第十九站:进程替换

文章目录 一、单进程版---最简单的程序替换二、进程替换的原理三、多进程的程序替换1.多进程的程序替换实例2.那么程序在替换时候有没有创建子进程呢3.再谈原理4.一个现象5.我们的CPU如何得知程序的入口地址? 四、各个接口的介绍1.execl2.execlp3.execv4.execvp5.ex…

Navicat 基于 GaussDB 主备版的快速入门

Navicat Premium(16.2.8 Windows版或以上) 已支持对GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能,还提供强大的高阶功能(如模型、结构同步、协同合作、数据迁移等),这…

BGP的基础知识

BGP——边界网关协议 IGP——内部网关协议——OSPF、RIP、ISIS EGP——外部网关协议——EGP、BGP 边界网关协议BGP是一种实现自治系统AS之间的路由可达,并选择最佳路由的路径矢量路由协议。目前在IPV4环境下主要使用BGPV4,目前市场上也存在BGPV4&…

光谱融合——Deep Spatio-spectral Attention Convolutional Neural Networks

Hyperspectral Image Super-Resolution via Deep Spatiospectral Attention Convolutional Neural Networks 简介 论文链接 对于高分辨率多光谱(高分辨率意味空间信息更好)图像,采用spatial attention机制进行空间信息的保留,对…

【latex】公式推导等号对齐

使用aligned进行多行公式对齐,&作为对齐的节点,\作为公式换行 \begin{equation} \begin{aligned}a& bc \\& cd \end{aligned} \end{equation}

ChatGPT API 学习

参考:从零开始的 ChatGPT API 使用指南,只需三步! - 知乎 (zhihu.com) ChatGPT API 是一种由 OpenAI 提供的 API,它可以用最简单的方式把 ChatGPT 的聊天能力接入到各种应用程序或服务中。 自然语言语音识别(Natural Language S…

CPU/GPU实现向量内积

向量内积&#xff08;点乘/点积/数量积&#xff09;&#xff1a;两个向量对应元素相乘之后求和&#xff1a; CPU实现&#xff1a; //cpu 实现一下向量内积#include<stdio.h> template<typedef T> void dot_mul(T *a, T *b, T *c, int n) { double tmp 0;for(i…

三层交换机实现不同VLAN间通讯

默认时&#xff0c;同一个VLAN中的主机才能彼此通信&#xff0c;那么交换机上的VLAN用户之间如何通信&#xff1f; 要实现VLAN之间用户的通信&#xff0c;就必须借助路由器或三层交换机来完成。 下面以三层交换机为例子说明&#xff1a; 注意&#xff1a; 1.交换机与三层交换…

uni-app 使用vscode开发uni-app

安装插件 uni-create-view 用于快速创建页面 配置插件 创建页面 输入页面名称&#xff0c;空格&#xff0c;顶部导航的标题&#xff0c;回车 自动生成页面并在pages.json中注册了路由 pages\login\login.vue <template><div class"login">login</d…

python-opencv 培训课程笔记(1)

python-opencv 培训课程笔记&#xff08;1&#xff09; 博主参加了一次opencv库的培训课程&#xff0c;把课程所学整理成笔记&#xff0c;供大家学习&#xff0c;第一次课程包括如下内容&#xff1a; 1.读取图像 2.保存图像 3.使用opencv库显示图像 4.读取图像为灰度图像 …

现货白银MACD实战分析例子

MACD这个技术指标的全称是平滑异同移动平均线&#xff0c;主要表示经过平滑处理后均线的差异程度&#xff0c;一般用来研判现货白银价格变化的方向、强度和趋势。MT4中的MACD指标&#xff0c;主要是由信号线、&#xff08;上升/下跌&#xff09;动能柱、0轴这三部分组成。 MACD…

9、传统计算机视觉 —— 边缘检测

本节介绍一种利用传统计算机视觉方法来实现图片边缘检测的方法。 什么是边缘检测? 边缘检测是通过一些算法来识别图像中物体之间,或者物体与背景之间的边界,也就是边缘。 边缘通常是图像中灰度变化显著的地方,标志着不同区域的分界线。 在一张图像中,边缘可以是物体的…

新能源充电桩工业4G路由器应用,推动绿色出行,响应环保理念

在智慧城市环保事业发展领域&#xff0c;新能源技术应用成熟&#xff0c;物联网技术越来越广泛&#xff0c;充电桩物联网成为了智慧城市建设的热门应用。充电桩作为新能源汽车的重要配套设施&#xff0c;对于节能减排和推动环保理念可持续发展具有重要意义。而工业4G路由器作为…

css 实现文字流光效果

经过调研发现大多滑块验证码中&#xff0c;有一些文字流光效果&#xff0c;因此在这里简单实现一下。 实现主要利用background 渐变背景以及backgorund-clip:text实现。具体代码如下 css部分 .slide {width: 300px;height: 40px;border: 1px solid #ccc;border-radius: 8px;…