当前位置: 首页 > news >正文

多线程——线程安全的练习加感悟

模拟抢票系统

有三个类:
Main:

package Test.ThreadSafeSimulation;public class Main {public static void main(String[] args) {RaceTicket raceTicket1 = new RaceTicket();RaceTicket raceTicket2 = new RaceTicket();raceTicket1.setName("线程1");raceTicket2.setName("线程2");raceTicket1.start();raceTicket2.start();}
}

RaceTicket:

package Test.ThreadSafeSimulation;public class RaceTicket extends Thread{//如果想要使用同步方法来起到锁的作用,那么几个线程类就不能继承thread,而应该采用接口,因为通过接口创建一个对象,然后依此创建几个线程,调用同步方法的都是一个,也就是唯一的一个那个接口对象。@Overridepublic void run() {for(int i = 0 ; i < 100 ; i++){System.out.println(Thread.currentThread().getName() + "抢到了第" + simulateBankTicket.ticketCount + "张票");simulateBankTicket.ticketCount ++;if(simulateBankTicket.ticketCount > 100){System.out.println("票已售完!!!");break;}}}
}

simulateBankTicket:

package Test.ThreadSafeSimulation;public class simulateBankTicket {static int ticketCount = 1;}

最终实现效果如下:
在这里插入图片描述
票与票之间会出现重复,漏票现象。
原理:这源于java的线程实际上是通过争夺CPU执行权来“摇摆”运行。
例如:
1.在线程1运行到simulateBankTicket.ticketCount ++;的时候,线程2此时抢夺到了CPU执行权,又运行了一次simulateBankTicket.ticketCount ++;此时就会发生漏票现象
2.在线程1运行到System.out.println(Thread.currentThread().getName() + “抢到了第” + simulateBankTicket.ticketCount + “张票”);的时候,线程2此时抢夺到了CPU执行权,又运行了一次。此时就会发生重复情况。

解决方法:锁 同步方法。

锁:

关键字synchronized:
代码格式:

synchronized(随便一个对象,但要保证要唯一){这里面是你想要保护不受别的线程干扰的东西,例如多个线程都要操作的某个变量。
}

括号里面的东西叫锁,它来保证别的线程不要打扰“闭关”的线程。并且这个锁要求对所有的线程都具有约束力,因此它必须要唯一。如果不唯一,造成的后果就是,每一个线程都携带了一把属于自己的锁,这个锁锁不住其他线程,因此没任何卵用。
实现代码如下:

package Test.ThreadSafeSimulation;public class RaceTicket extends Thread{//如果想要使用同步方法来起到锁的作用,那么几个线程类就不能继承thread,而应该采用接口,因为通过接口创建一个对象,然后依此创建几个线程,调用同步方法的都是一个,也就是唯一的一个那个接口对象。@Overridepublic void run() {for(int i = 0 ; i < 100 ; i++){synchronized (simulateBankTicket.object) {System.out.println(Thread.currentThread().getName() + "抢到了第" + simulateBankTicket.ticketCount + "张票");simulateBankTicket.ticketCount++;if (simulateBankTicket.ticketCount > 100) {System.out.println("票已售完!!!");break;}}}}
}

在另一个类里面定义唯一锁:

package Test.ThreadSafeSimulation;public class simulateBankTicket {static Object object = new Object();static int ticketCount = 1;
}

注意这个锁一定不能为空。
实现效果如下:
在这里插入图片描述

同步方法:

先如此做:

package Test.ThreadSafeSimulation;public class RaceTicket extends Thread{//如果想要使用同步方法来起到锁的作用,那么几个线程类就不能继承thread,而应该采用接口,因为通过接口创建一个对象,然后依此创建几个线程,调用同步方法的都是一个,也就是唯一的一个那个接口对象。@Overridepublic void run() {lock();}public synchronized void lock(){for(int i = 0 ; i < 100 ; i++){System.out.println(Thread.currentThread().getName() + "抢到了第" + simulateBankTicket.ticketCount + "张票");simulateBankTicket.ticketCount++;if (simulateBankTicket.ticketCount > 100) {System.out.println("票已售完!!!");break;}}}
}

实现效果如下:
在这里插入图片描述
发现没解决问题。
原理:同步方法的真实代码:this.lock(); this在这个过程中被隐去了。this指的就是本线程的对象。而对于继承了线程类的RaceTicket创建出来的2个对象,执行到输出语句和增加票数的代码的时候,都是自顾自的在调用lock()方法,本质上和刚才锁的不唯一的出错逻辑一样,都没有办法锁住其他线程。
如何改正?
答案是:
使用接口创建线程对象:
实现代码如下:

package Test.ThreadSafeSimulation;public class Main {public static void main(String[] args) {
//        RaceTicket raceTicket1 = new RaceTicket();
//        RaceTicket raceTicket2 = new RaceTicket();
//        raceTicket1.setName("线程1");
//        raceTicket2.setName("线程2");
//
//        raceTicket1.start();
//        raceTicket2.start();RaceTicket r1 = new RaceTicket();new Thread(r1,"线程1").start();new Thread(r1,"线程2").start();}
}
package Test.ThreadSafeSimulation;public class RaceTicket implements Runnable{//如果想要使用同步方法来起到锁的作用,那么几个线程类就不能继承thread,而应该采用接口,因为通过接口创建一个对象,然后依此创建几个线程,调用同步方法的都是一个,也就是唯一的一个那个接口对象。@Overridepublic void run() {lock();}public synchronized void lock(){for(int i = 0 ; i < 100 ; i++){System.out.println(Thread.currentThread().getName() + "抢到了第" + simulateBankTicket.ticketCount + "张票");simulateBankTicket.ticketCount++;if (simulateBankTicket.ticketCount > 100) {System.out.println("票已售完!!!");break;}}}
}

这也是为什么如果要用同步方法的话,需要使用接口来创建线程对象了,因为只有包装了同一个接口对象的线程,在调用同步方法的时候,可以成功锁住其他线程对象。因为这个接口对象是唯一的。

http://www.dtcms.com/a/392358.html

相关文章:

  • 使用 TwelveLabs 的 Marengo 视频嵌入模型与 Amazon Bedrock 和 Elasticsearch
  • Windows 11 下 Notepad++ 等应用无法启动问题排查修复
  • 面向口齿不清者的语音识别新突破:用大模型拯救“听不懂”的声音
  • 服装企业优化信息化管理系统的最佳软件选择
  • 多阶段构建镜像
  • 推荐一个开源服务器一键自动重装系统脚本:reinstall
  • 【C++进阶】C++11 的新特性 | lambda | 包装器
  • 2.【QT 5.12.12 安装 Windows 版本】
  • Rust_2025:阶段1:day6.3 macro
  • 【Qt开发】输入类控件(一)-> QLineEdit
  • python10——组合数据类型(集合)
  • 分布式专题——14 RabbitMQ之集群实战
  • WEEX唯客的多维度安全守护
  • 深度学习环境配置
  • 生鲜速递:HTTP 的缓存控制
  • ​​Snipaste 2.10.1.dmg截图安装教程|Mac电脑拖拽安装详细步骤​
  • 10.1.1 使用python完成第一个遗传算法
  • C语言内存精讲系列(二十九):C 语言堆区内存进阶与动态内存实战
  • 6G量子通信融合:破解未来网络的安全与效能密码
  • C#练习题——泛型实现单例模式和增删改查
  • 网关登录校验
  • Kubernetes Fluent Bit Pod Pending 问题解决方案
  • 我爱学算法之—— 位运算(中)
  • 什么是差分信号
  • 相机标定(Camera Calibration)原理及步骤:从 “像素模糊” 到 “毫米精准” 的关键一步
  • 用 【C# + WinUI3 + 图像动画】 来理解:高数 - 函数 - 初等函数
  • ​​[硬件电路-296]:单刀双掷(SPDT)模拟开关
  • 【MAVLink】MAVSDK编程入门、基本概念
  • MAC-基于反射的枚举工具类优化
  • 防御性编程:编程界的‘安全驾驶‘指南