多线程学习之解决线程同步的实现方法

 一、卖票的多线程实现

需求:共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

代码实现:

/*** @Author:kkoneone11* @name:SellTicket1* @Date:2023/8/26 11:32*/
public class SellTicket1 implements Runnable{private int tickets = 100;@Overridepublic void run() {while(true){if(tickets < 0){break;}else {try{Thread.sleep(100);}catch (Exception e){e.printStackTrace();}tickets--;System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);}}}
}public class SellTicketDemo {public static void main(String[] args) {SellTicket1 st = new SellTicket1();Thread thread1 = new Thread(st, "窗口1");Thread thread2 = new Thread(st, "窗口2");Thread thread3 = new Thread(st, "窗口3");thread1.start();thread2.start();thread3.start();}
}

可以看到这种程序写法的问题有:

  • 相同的票出现了多次

  • 出现了负数的票  

 问题产生的原因分析:这种多线程共享的是同一份数据,线程执行的随机性导致的,可能在卖票过程中丢失cpu的执行权,导致出现问题

二、解决问题的方案

要解决这个问题实际上就是让程序没有安全问题,如何实现其实就是让每次操作的时候只能有一个线程执行成功即可,那么可以实现的方案如下:

同步代码块

实现方法:

synchronized(任意对象) { 多条语句操作共享数据的代码 
}

优缺点:

  • 好处:解决了多线程的数据安全问题

  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

实例:

public class SellTicket1 implements Runnable{private int tickets = 100;private Object obj = new Object();@Overridepublic void run() {while(true){synchronized (obj){//当线程进来的时候就会把这段代码锁起来if(tickets <= 0){break;}else {try{Thread.sleep(100);}catch (Exception e){e.printStackTrace();}tickets--;System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);}}//到此处锁就会释放了}}
}public class SellTicketDemo {public static void main(String[] args) {SellTicket1 st = new SellTicket1();Thread thread1 = new Thread(st, "窗口1");Thread thread2 = new Thread(st, "窗口2");Thread thread3 = new Thread(st, "窗口3");thread1.start();thread2.start();thread3.start();}
}

同步方法

实现方法:

锁住的对象是:this

修饰符 synchronized 返回值类型 方法名(方法参数) { 方法体;
}

静态同步方法

实现方法:

锁住的对象是:类名.class

修饰符 static synchronized 返回值类型 方法名(方法参数) { 方法体;
}

实例:

public class SellTicket1 implements Runnable{private static int tickets = 100;@Overridepublic void run() {while(true){if("窗口一".equals(Thread.currentThread().getName())){//同步方法boolean b = synchronizedMthod();if(b){break;}}else if("窗口二".equals(Thread.currentThread().getName())){//同步代码块synchronized (SellTicket1.class){if(tickets == 0){break;}else{try{Thread.sleep(100);}catch (Exception e){e.printStackTrace();}tickets--;System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);}}}}}private static synchronized boolean synchronizedMthod(){if(tickets == 0){return true;}else{try{Thread.sleep(100);}catch (Exception e){e.printStackTrace();}tickets--;System.out.println(Thread.currentThread().getName() + "票数还剩余" + tickets);return false;}}
}public class SellTicketDemo {public static void main(String[] args) {SellTicket1 st = new SellTicket1();Thread thread1 = new Thread(st, "窗口1");Thread thread2 = new Thread(st, "窗口2");Thread thread3 = new Thread(st, "窗口3");thread1.start();thread2.start();thread3.start();}
}

总结:

无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。

ReentrantLock()

如果我们想可以直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

方法名说明
void lock()获得锁
void unlock()释放锁

实例:

public class SellTicket1 implements Runnable{//票的数量private int tickets = 100;private Object obj = new Object();private ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {while (true) {//synchronized (obj){//多个线程必须使用同一把锁.try {lock.lock();if (tickets <= 0) {//卖完了break;} else {Thread.sleep(100);tickets--;System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + tickets + "张票");}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}// }}}
}public class SellTicketDemo {public static void main(String[] args) {SellTicket1 st = new SellTicket1();Thread thread1 = new Thread(st, "窗口1");Thread thread2 = new Thread(st, "窗口2");Thread thread3 = new Thread(st, "窗口3");thread1.start();thread2.start();thread3.start();}
}

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

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

相关文章

wsl Ubuntu中非root的普通用户怎么直接执行docker命令

docker需要root权限&#xff0c;如果希望非root用户直接使用docker命令&#xff0c;而不是使用sudo&#xff0c;可以选择将该用户加入到docker用户组。 sudo groupadd docker&#xff1a;添加到groupadd用户组&#xff08;已经有docker用户组&#xff0c;所以可以不用再新增do…

电子电路学习笔记之NCP304LSQ37T1G ——超低电流电压检测器

超低电流电压检测器是一种专门用于检测极小电流值的设备。它们常用于电子元件或电路中&#xff0c;用于监测电流的存在和程度。这些检测器通常具有高灵敏度和高精度&#xff0c;能够测量微安级别或更小的电流。 超低电流电压检测器的应用领域广泛&#xff0c;例如电池管理系统…

vue实现自定义树形组件

欢迎点击关注-前端面试进阶指南&#xff1a;前端登顶之巅-最全面的前端知识点梳理总结 *分享一个使用比较久的&#x1fa9c; 效果展示&#xff1a; 近期的一个功能需求&#xff0c;实现一个树形结构&#xff1a;可点击&#xff0c;可拖拽&#xff0c;右侧数据可以拖拽到对应的…

前端刷题-Promise系列

Promise系列 promise.all // 定义 Promise.all function (promises) {let count 0;let result [];return new Promise((resolve, reject) > {for (let i 0; i < promises.length; i) {promises[i].then((res) > {count;result[i] res;if (count promises.leng…

六、Json 数据的交互处理

文章目录 一、JSON 数据的交互处理1、为什么要使用 JSON2、JSON 和 JavaScript 之间的关系3、前端操作 JSON3.1 JavaScript 对象与 JSON 字符串之间的相互转换 4、JAVA 操作 JSON4.1 Json 的解析工具&#xff08;Gson、FastJson、Jackson&#xff09;4.2 ResponseBody 注解、Re…

Fedora Linux 的家族(一):官方版本

导读本文将对 Fedora Linux 官方版本进行更详细的介绍。共有五个 版本&#xff1a; Fedora Workstation、Fedora Server、Fedora IoT、Fedora CoreOS 和 Fedora Silverblue。Fedora Linux 下载页面目前显示其中三个为 官方 版本&#xff0c;另外两个为 新兴 版本。本文将涵盖所…

LOIC(low orbit ion cannon)

前言 重要的话说三遍&#xff1a; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上…

【C++初阶】list的常见使用操作

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…

关闭jenkins插件提醒信息

jenkins提醒信息和安全警告可以帮助我们了解插件或者jenkins的更新情况&#xff0c;但是有些插件是已经不维护了&#xff0c;提醒却一直存在&#xff0c;看着糟心&#xff0c;就像下面的提示 1、关闭插件提醒 找到如下位置&#xff1a;系统管理-系统配置-管理监控配置 打开管…

线性代数的学习和整理11: 子式与余子式

目录 1 原始矩阵A 2 子式&#xff08;都是行列式&#xff09; 2.1 k阶子式&#xff08;行数列数即可&#xff09; 比如1阶子式&#xff1a;因为只有1行1列 比如2阶子式&#xff1a;因为有2行2列 比如3阶子式&#xff1a;因为有3行3列 2.2 k阶主子式 {行序号数组} {列序号…

java对时间序列根据阈值进行连续性分片

问题描述&#xff1a;我需要对一个连续的时间戳list进行分片&#xff0c;分片规则是下一个数据比当前数据要大于某一个阈值则进行分片&#xff1b; 解决方式&#xff1a; 1、输入的有顺序的list &#xff0c;和需要进行分片的阈值 2、调用方法&#xff0c;填入该排序的list和阈…

阿里云轻量应用服务器Linux-Centos7下Oracle19c的配置

初始环境&#xff1a;阿里云轻量应用服务器已经安装Oracle19c 具体目标&#xff1a;配置Oracle Database 19c 目录 第一步&#xff1a;切换到Oracle命令行第二步&#xff1a;新建用户和表空间第三步&#xff1a;切换用户第四步&#xff1a;在当前用户下创建一些表第五步&#x…

SQL Server 2019导入txt数据

1、选择导入数据 2、选择Flat file Source 选择文件&#xff0c;如果第一行不是列名&#xff0c;就不勾选。 3、下一步 可以看看数据是否是对的 4、下一步 选择SQL server Native Client 11&#xff0c;数据库选择导入进的库 输入连接数据库的名字和要导入的数据库 下一…

【Jetpack】Navigation 导航组件 ⑤ ( NavigationUI 类使用 )

文章目录 一、NavigationUI 类简介二、NavigationUI 类使用流程1、创建 Fragment2、创建 NavigationGraph3、Activity 导入 NavHostFragment4、创建菜单5、Activity 界面开发 NavigationUI 的主要逻辑 ( 重点 )a、添加 Fragment 布局b、处理 Navigation 导航逻辑 ( 重点 )c、启…

螺旋矩阵-2

import numpy as np def func(n):rotate_list np.zeros(n * n).reshape(n, n).tolist()flag rightx 0y 0i 1while i < n * n:if flag right:try:if rotate_list[y][x] 0:rotate_list[y][x] ix 1i 1else:# 不为0时改变递增方向flag downx - 1y 1except Exception…

设计模式--工厂模式(Factory Pattern)

一、 什么是工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种创建对象的接口&#xff0c;但是将对象的实例化过程推迟到子类中。工厂模式允许通过调用一个共同的接口方法来创建不同类型的对象&#xff0c;而无需暴露对…

android 下载网络文件

工具类 import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; import android.os.Environment; import android.util.Log;import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import …

Ajax复习

Ajax复习 一、简介 ​ AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML。 ​ 一句话总结&#xff1a;无刷新通信。 二、 特点 优点 无刷新通信 允许你根据用户事件来更新部分页面内容 缺点 没有浏览历史&#xff0c;不能回退 存在跨域问题…

中国肉羊产业链调研与投资评估析报告(2023版)

内容简介&#xff1a; 肉羊是适应外界环境较强的家畜之一&#xff0c;食性广、耐粗饲、抗逆性强。饲养肉羊投资少、周转快、效益稳、回报率高。近年来&#xff0c;国内外羊肉市场发生了一些变化&#xff0c;为肉羊产业的发展提供了巨大空间&#xff0c;使肉羊生产正成为一个黄…

Python爬虫武汉市二手房价格数据采集分析:Linear Regression、XGBoost和LightGBM|代码分享...

全文链接&#xff1a;http://tecdat.cn/?p31958 分析师&#xff1a;Yan Liu 我国有大量的资金都流入了房地产行业&#xff0c;同时与其他行业有着千丝万缕的联系&#xff0c;可以说房地产行业对推动我国深化改革、经济发展、工业化和城市化具有不可磨灭的作用&#xff08;点击…