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

5.2 线程实际案例练习


文章目录

  • 1.概述
  • 2.实现方案一:继承Thread
    • 2.1 代码实现
    • 2.2 代码分析
  • 3.实现方案二:实现Runnable接口
    • 3.1 代码实现
    • 3.2 代码分析
  • 4.实现方案三:构建线程池
    • 4.1 代码实现
    • 4.2 代码分析


1.概述

接下来我们通过一个售票案例的实际操作来深入理解线程的相关应用;

需求:设计4个售票窗口,总计售票100张,使用多线程的方式进行售卖,当票售完后,停止售卖,在控制台打印具体为哪个线程售卖了第多少张票;

2.实现方案一:继承Thread

首先,我们通过简单的方法——继承Thread类来实现这一功能;

2.1 代码实现

package partFour;
/*设计多线程编程模型,4个窗口购机售票100张* 本方案使用多线程编程方案1,继承Thread类的方式来完成*/
public class TestThreat {public static void main(String[] args) {//5.创建多个线程对象TicketsThread t1 = new TicketsThread();TicketsThread t2= new TicketsThread();TicketsThread t3 = new TicketsThread();TicketsThread t4 = new TicketsThread();//6.以多线程的方式启动t1.start();t2.start();t3.start();t4.start();}
}
//1.自定义多线程售票类,继承Thread
class TicketsThread extends Thread{//3.定义变量,保存要卖的票数,需要设置静态,不然4个线程对象每个线程对象都会有100张票/*4个线程对象每个线程对象售票400张,原因是创建了4次对象,各自操作各自的成员变量* 解决:让所有对象共享同一个数据,票数需要设置为静态*/static int tickets = 100;//2.重新run方法@Overridepublic void run(){//4.1循环卖票,使用while循环,方便后续演示容易出错的位置while (true){//7.让每个线程经历休眠,增加线程状态切换频率与出错的概率//问题1:产生重卖的现象,同一张票卖给多人//问题2:产生了超卖的现象,超出了规定的票数100,出现了0,-1,-2try {Thread.sleep(10);//让当前线程休眠10ms} catch (InterruptedException e) {e.printStackTrace();}//4.2打印当前正在卖票的线程名称,并且票数-1System.out.println(getName()+"="+tickets--);//4.3做判断,如果没有票了,就退出死循环if (tickets<=0) break;//注意,死循环一定要设置出口}}
}

2.2 代码分析

  1. 每次创建线程对象,都会生成一个tickets变量值是100,创建4次对象就生成了400张票了。不符合需求,怎么解决呢?
    能不能把tickets变量在每个对象间共享,就保证多少个对象都是卖这100张票。
    解决方案: 用静态修饰
  2. 产生超卖,0 张 、-1张、-2张。
  3. 产生重卖,同一张票卖给多人。
  4. 多线程安全问题是如何出现的?常见情况是由于线程的随机性+访问延迟。
  5. 以后如何判断程序有没有线程安全问题?
    在多线程程序中 + 有共享数据 + 多条语句操作共享数据就一定会有线程的并发安全问题,就一定考虑如何避免或解决这个问题;
    在目前,我们只能考虑使用同步锁解决;

3.实现方案二:实现Runnable接口

接下来,我们将使用更复杂一点的Runnable接口来实现此功能;

3.1 代码实现

package partFour;
/*需求:设计多线程编程模型,4个窗口共计售票100张* 本方案使用多线程编程方案2,实现Runnable接口的方式来完成*/
public class TestRunnable {public static void main(String[] args) {//5.创建Runnable接口的实现类对象,作为目标业务对象TicketsRunnable target = new TicketsRunnable();//6.创建多个Thread类线程对象,并将target业务交给多个线程对象来处理Thread t1 = new Thread(target);Thread t2 = new Thread(target);Thread t3 = new Thread(target);Thread t4 = new Thread(target);//7.以多线程的方式启动多个线程对象t1.start();t2.start();t3.start();t4.start();}
}//1.自定义多线程类实现Runnable接口
class TicketsRunnable implements Runnable{//3.定义一个成员变量,用来保存票数100/*由于自定义类对象只创建了一次,所以票数被所有线程对象Thread共享* 不需要添加static,只卖了100张票,不会卖400次*/int tickets = 100;//2.添加接口中未实现的方法,方法里是我们的业务@Overridepublic void run(){//4.1循环卖票while (true){//8.让每个线程经历休眠,增加线程状态切换频率与出错的概率//问题1:产生重卖的现象,同一张票卖给多人//问题2:产生了超卖的现象,超出了规定的票数100,出现了0,-1,-2try {Thread.sleep(10);//让当前线程休眠10ms} catch (InterruptedException e) {e.printStackTrace();}//4.2打印当前正在售票的线程名称 & 票数-1System.out.println(Thread.currentThread().getName()+"="+tickets--);//4.3设置死循环的出口,没票了就停止卖票if(tickets<=0)break;}}
}

3.2 代码分析

  1. 实现Runnable接口中,因为只创建了一个对象,所以并不会生成多个tickets变量的值,所以此处我们不需要使用静态来修饰变量
  2. 同样产生超卖,0 张 、-1张、-2张。
  3. 同样产生重卖,同一张票卖给多人。
  4. 同样产生线程的并发安全问题;

4.实现方案三:构建线程池

如果在程序中创建大量的生命周期很短的线程,这会对性能产生比较大的影响,构建一个新的线程还算是一个比较大的开销;

此时,我们可以利用线程池很好的去解决这个问题,这样我们就不必将每个任务都映射到一个单独的线程上了。

线程池中会包含很多准备运行的线程,为线程池提供一个Runnable,就会有一个线程调用run方法,当run方法执行完毕后,线程并不会死亡,而是继续在池中等待下一个请求的调用。

我们通常使用Executors用来辅助创建线程池的工具类,常用的方法是:newFixedThreadPoo(int),这个方法可以帮我们创建指定数目线程的线程池;

4.1 代码实现

package partFour;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/* 本类用于回顾多线程售票案例2*/
public class TestRunnablevPool {public static void main(String[] args) {//1.创建实现类也就是目标业务对象TicktRunnable2 target = new TicktRunnable2();//2.使用Excutors创建最多包含5个线程的线程池--ExcutorService/*Executors是用来辅助创建线程池的工具类* 常用的方法是:newFixedThreadPoo(int)这个方法可以帮我们创建指定书目线程的线程池*/ExecutorService pool = Executors.newFixedThreadPool(5);//3.使用循环,启动线程for(int i=0; i<5; i++){/*execute让线程池中的线程来执行业务* 每次调用这个方法,都会将一个线程加到就绪队列中* 这个方法的参数,就是我们要执行的具体业务,也就是目标业务类对象target*/pool.execute(target);}}
}//1.创建接口实现类,作为目标业务量
class TicktRunnable2 implements Runnable{//3.1定义票数int tickets = 100;//8.注意在外部添加一个唯一的对象,Object o = new Object();//2.添加父接口中未实现的抽象方法,里面是业务@Overridepublic void run() {//3.2循环卖票while (true) {if (tickets>0)//7.为了增加线程状态切换概率与出错频率,在售票前休眠10毫秒try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("窗口" + Thread.currentThread().getName() + "售票" + tickets--);if (tickets <= 0) break;}}
}

4.2 代码分析

  1. 通过构建线程池可以很好的解决资源浪费的问题
  2. 虽然使用了双重校验,但还是存在超卖,重卖的问题

相关文章:

5.2 线程实际案例练习

文章目录1.概述2.实现方案一&#xff1a;继承Thread2.1 代码实现2.2 代码分析3.实现方案二&#xff1a;实现Runnable接口3.1 代码实现3.2 代码分析4.实现方案三&#xff1a;构建线程池4.1 代码实现4.2 代码分析1.概述 接下来我们通过一个售票案例的实际操作来深入理解线程的相…...

stm32f407探索者开发板(十七)——串口寄存器库函数配置方法

文章目录一、STM32串口常用寄存器和库函数1.1 常用的串口寄存器1.2 串口相关的库函数1.3 状态寄存器&#xff08;USART_ SR&#xff09;1.4 数据寄存器&#xff08;USART_ DR&#xff09;1.5 波特率寄存器&#xff08;USART_BRR&#xff09;二、串口配置一般步骤一、STM32串口常…...

山西省2023年软考报名3月14日开始

根据2023年上半年计算机技术与软件专业技术资格(水平)考试工作计划&#xff0c;可以得知&#xff0c;全国考务管理服务平台将于2023年3月13日开放&#xff0c;各地开始组织报名&#xff0c;如山西已发布2023上半年报名简章&#xff0c;从3月14号开始报名。 软考报名官网 大部…...

进程章节总结性实验

进程实验课笔记 本节需要有linux基础&#xff0c;懂基本的linux命令操作即可。 Ubuntu镜像下载 https://note.youdao.com/s/VxvU3eVC ubuntu安装 https://www.bilibili.com/video/BV1j44y1S7c2/?spm_id_from333.999.0.0 实验环境ubuntu22版本&#xff0c;那个linux环境都可以…...

【MyBatis】MyBatis的缓存

10、MyBatis的缓存 10.1、MyBatis的一级缓存 一级缓存是SqlSession级别的&#xff0c;通过同一个SqlSession查询的数据会被缓存&#xff0c;下次查询相同的数据&#xff0c;就会从缓存中直接获取&#xff0c;不会从数据库重新访问 使一级缓存失效的四种情况&#xff1a; 不…...

MyBatis基本使用

一、简介 MyBatis 中文文档 https://mybatis.org/mybatis-3/zh/index.html 1.什么是 MyBatis 概述&#xff1a;MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBa…...

如何运行YOLOv6的代码实现目标识别?

YOLOv6是由美团视觉团队开发的1.环境配置我们先把YOLOv6的代码clone下来git clone https://github.com/meituan/YOLOv6.git安装一些必要的包pip install pycocotools2.0作者要求pytorch的版本是1.8.0,我的环境是1.7.0&#xff0c;也是可以正常运行的pip install -r requirement…...

新品BCM6755A1KFEBG/MT7921LE/MT7921AU WiFi芯片

博通在WiFi市场具有相当的实力。在WiFi6上有下面这几个解决方案&#xff1a;型号&#xff1a;BCM6755 BCM6755A1KFEBG类型&#xff1a;四核1.5GHz CPU封装&#xff1a;BGA批次&#xff1a;新BCM6755和BCM6750还是A7架构&#xff0c;更多的用在中低端型号上。BCM6755和BCM6750 C…...

析构函数、拷贝构造

1、析构函数析构函数的定义方式函数名和类名相同&#xff0c;在类名前加~&#xff0c;没有返回值类型&#xff0c;没有函数形参&#xff08;不能重载&#xff09;当对象生命周期结束的时候&#xff0c;系统会自动调用析构函数先调用析构函数&#xff0c;再释放对象的空间析构函…...

光学镜头是制作过程阶段理解

光学镜头是由多组镜片组合而成&#xff0c;它是摄影机投影一及显微镜上必不可少的部件。那么光学镜头是如何制造的呢&#xff1f;光学镜头的制作分为以下四个阶段&#xff1a;第一、首先将一大块光学玻璃用钻石锯片进行切片&#xff0c;然后用钻头在每一块玻璃切片上钻出多块冰…...

实验室设计|实验室设计要点SICOLAB

一、实验室设计规划要素1、实验室布局&#xff1a;实验室的布局要符合实验室工作流程&#xff0c;可以将实验室划分为干净区和污染区&#xff0c;以确保实验室的卫生和实验的准确性。2、设备选购&#xff1a;根据实验需要选择适当的设备&#xff0c;并确保设备的质量和性能符合…...

I.MX6ULL_Linux_系统篇(16) uboot分析-启动流程

原文链接&#xff1a;I.MX6ULL_系统篇(16) uboot分析-启动流程 – WSY Personal Blog (cpolar.cn) 前面我们详细的分析了 uboot 的顶层 Makefile&#xff0c;了解了 uboot 的编译流程。本章我们来详细的分析一下 uboot 的启动流程&#xff0c;理清 uboot 是如何启动的。通过对 …...

【C#】async关键字修饰后有无await的影响

文章目录测试总结拓展&#xff1a;js的async await问题参考测试 来自微软官网的说法&#xff1a; 异步方法通常包含 await 运算符的一个或多个匹配项&#xff0c;但缺少 await 表达式不会导致编译器错误。 如果异步方法未使用 await 运算符标记悬挂点&#xff0c;则该方法将作…...

Interspeech2022 | 一种基于元辅助学习的低资源口语语义理解方法

中国移动研究院首席科学家冯俊兰博士带领人工智能与智慧运营中心语音团队共同撰写的文章《Meta Auxiliary Learning for Low-resource Spoken Language Understanding》被语音国际顶会Interspeech2022接收。 关于Interspeech Interspeech 是国际最大且最全面关于言语科学与技…...

File类的用法和InputStream,OutputStream的用法

这里写自定义目录标题一、File类1.构造方法2.普通方法二、InputStream1.方法2.FileInputStream3.Scanner类的应用三、OutputStream1.方法2.FileOutputStream3.PrintWriter类的应用一、File类 1.构造方法 签名说明File(File parent, Stringchild)根据父目录 孩子文件路径&…...

Java多线程——Thread类的基本用法

一.线程的创建继承Thread类//继承Thread类class MyThread extends Thread{Overridepublic void run() {System.out.println("线程运行的代码");} } public class Demo1 {public static void main(String[] args) {MyThread t new MyThread();t.start();//启动线程&a…...

【C++】类和对象练习——日期类的实现

文章目录前言1. 日期的合法性判断2. 日期天数&#xff08;/&#xff09;2.1 和的重载2.2 对于两者复用的讨论3. 前置和后置重载4. 日期-天数&#xff08;-/-&#xff09;5. 前置- -和后置- -的重载6. 日期-日期7. 流插入<<重载8. 流提取>>重载9. 总结10. 源码展示前…...

[LeetCode周赛复盘] 第 333 场周赛20230219

[LeetCode周赛复盘] 第 333 场周赛20230219 一、本周周赛总结二、 [Easy] 6362. 合并两个二维数组 - 求和法1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6365. 将整数减少到零需要的最少操作数1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6364. 无平方子集计数1. 题目描…...

数字化时代,如何做好用户体验与应用性能管理

引言 随着数字化时代的到来&#xff0c;各个行业的应用系统从传统私有化部署逐渐转向公有云、行业云、微服务&#xff0c;这种变迁给运维部门和应用部门均带来了较大的挑战。基于当前企业 IT 运维均为多部门负责&#xff0c;且使用多种运维工具&#xff0c;因此&#xff0c;当…...

Python爬虫(7)selenium3种弹窗定位后点击操作,解决点击登录被隐藏iframe无法点击的登陆问题

之前的文章有关于更多操作方式详细解答&#xff0c;本篇基于前面的知识点进行操作&#xff0c;如果不了解可以先看之前的文章 Python爬虫&#xff08;1&#xff09;一次性搞定Selenium(新版)8种find_element元素定位方式 Python爬虫&#xff08;2&#xff09;-Selenium控制浏览…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...