多线程合并练习题,线程安全(售票任务引入)--学习JavaEE的day30

day30

练习(day29)

注意代码注释,里面涉及代码实现遇到问题及解决方案,由于理解方便没有单独出来

1.计算任务

1.计算任务,一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果。

使用线程类

public class MyThread extends Thread{private int startIndex;//开始下标(包含)private int endIndex;//结束下标(不包含)private int[] arr;//外界数组public MyThread(int startIndex, int endIndex, int[] arr) {this.startIndex = startIndex;this.endIndex = endIndex;this.arr = arr;}private int sum;private boolean flag = true;@Overridepublic void run() {for (int i = startIndex; i < endIndex; i++) {sum += arr[i];}flag = false;}public int getSum() {return sum;}public boolean isFlag() {return flag;}
}
public class Test01 {public static void main(String[] args) throws InterruptedException {//创建数组int[] arr = new int[20000];//初始化数组数据 -- {1,2,3,....,20000}for (int i = 0; i < arr.length; i++) {arr[i] = i+1;}//创建线程MyThread t1 = new MyThread(0, 5000, arr);MyThread t2 = new MyThread(5000, 10000, arr);MyThread t3 = new MyThread(10000, 15000, arr);MyThread t4 = new MyThread(15000, 20000, arr);//启动线程t1.start();t2.start();t3.start();t4.start();//问题出现原因:四个子线程还没有运行完毕,就被主线程抢到CPU资源了//解决思路:让四个子线程执行完毕后,主线程才能执行 --- 主线程阻塞!!!//解决方案一:休眠电脑算完后,就能得到准确的结果;(主线程阻塞,拿不到cpu资源)
//		Thread.sleep(6);//解决方案二:判断子线程是否执行完,有一个没执行完,主线程都不会输出;(主线程可以拿到cpu资源)
//		while(t1.isFlag() || t2.isFlag() || t3.isFlag() || t4.isFlag()){}//解决方案三:子线程join,主线程阻塞,四个子线程执行完毕,主线程才抢到cpu资源(方案2缺点:主线程未阻塞)t1.join();t2.join();t3.join();t4.join();//合并子线程的运行结果int result = t1.getSum() + t2.getSum() + t3.getSum() + t4.getSum();System.out.println(result);}
}

对于解决方案二:开始添加下面注释部分代码,即判断线程是否执行完毕的变量,和相关变量get方法

//	private boolean flag = true;@Overridepublic void run() {for (int i = startIndex; i < endIndex; i++) {sum += arr[i];}//		flag = false;}//	public int getSum() {
//		return sum;
//	}//	public boolean isFlag() {
//		return flag;
//	}

使用任务类

任务类更容易理解,线程合并,分拆了多个线程来进行并行计算,2万个整数分成几段

​ //创建任务
​ Task task1 = new Task(0, 5000, arr);
​ Task task2 = new Task(5000, 10000, arr);
​ Task task3 = new Task(10000, 15000, arr);
​ Task task4 = new Task(15000, 20000, arr);

public class Task implements Runnable{private int startIndex;private int endIndex;private int[] arr;public Task(int startIndex, int endIndex, int[] arr) {this.startIndex = startIndex;this.endIndex = endIndex;this.arr = arr;}private int sum;private boolean flag = true;@Overridepublic void run() {for (int i = startIndex; i < endIndex; i++) {sum += arr[i];}flag = false;}public int getSum() {return sum;}public boolean isFlag() {return flag;}}public class Test01 {public static void main(String[] args) throws InterruptedException {//创建数组int[] arr = new int[20000];//初始化数组数据 -- {1,2,3,....,20000}for (int i = 0; i < arr.length; i++) {arr[i] = i+1;}//创建任务Task task1 = new Task(0, 5000, arr);Task task2 = new Task(5000, 10000, arr);Task task3 = new Task(10000, 15000, arr);Task task4 = new Task(15000, 20000, arr);//创建线程Thread t1 = new Thread(task1);Thread t2 = new Thread(task2);Thread t3 = new Thread(task3);Thread t4 = new Thread(task4);//启动线程t1.start();t2.start();t3.start();t4.start();//问题出现原因:四个子线程还没有运行完毕,就被主线程抢到CPU资源了//解决思路:让四个子线程执行完毕后,主线程才能执行 --- 主线程阻塞!!!//解决方案一:
//		Thread.sleep(6);缺点:休眠不知道设置多少好//解决方案二:
//		while(task1.isFlag() || task2.isFlag() || task3.isFlag() || task4.isFlag()){}
//		缺点:主线程会抢到资源,只是条件原因而不会结束//解决方案三:t1.join();t2.join();t3.join();t4.join();//合并任务的运行结果int result = task1.getSum() + task2.getSum() + task3.getSum() + task4.getSum();System.out.println(result);}
}

注意:

主线程结束,子线程还会继续执行完成

2.售票任务(线程安全)

2.铁道部发布了一个售票任务,要求销售1000张票,要求有3个窗口来进行销售,请编写多线程程序来模拟这个效果(该题涉及到线程安全,https://www.jb51.net/article/221008.htm)

i. 窗口001正在销售第1张票

ii. 窗口001正在销售第2张票

iii. 窗口002正在销售第3张票

iv. 。。。

v. 窗口002正在销售第1000张票

涉及到线程安全,要加锁

使用线程类

public class MyThread extends Thread{private static int allTicket = 1000;private static int curTicket = 0;private static Object obj = new Object();public MyThread(String name) {super(name);}@Overridepublic void run() {while(curTicket < allTicket){//synchronized (String.class) {//synchronized ("abc") {synchronized (obj) {if(curTicket < allTicket){curTicket++;System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");}if(curTicket >= allTicket){System.out.println("窗口" +  Thread.currentThread().getName() + "票已经售完");}}}}
}public class Test01 {public static void main(String[] args) {MyThread t1 = new MyThread("001");MyThread t2 = new MyThread("002");MyThread t3 = new MyThread("003");t1.start();t2.start();t3.start();}
}
模拟过程出现问题
问题一:

三个窗口各卖1000张票,一共卖了3000张

出现原因:allTicket和curTicket是run方法的局部变量,三个线程抢到CPU资源后,都会调用run方法,run方法被调用了3次,所以卖了3000张票

解决方案:将allTicket和curTicket设置为静态变量,让三个线程共享

出现原因:
public class MyThread extends Thread{public MyThread(String name) {super(name);}@Overridepublic void run() {int allTicket = 1000;int curTicket = 0;while(curTicket < allTicket){curTicket++;System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");}}
}
解决方案:
public class MyThread extends Thread{private static int allTicket = 1000;private static int curTicket = 0;public MyThread(String name) {super(name);}@Overridepublic void run() {while(curTicket < allTicket){curTicket++;System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");}}
}
问题二:

有些票没有卖,有些票卖了重票

出现原因:当前线程抢到CPU资源后做了票的自增,但是还没来得及输出,时间片到了就退出CPU资源,然后其他线程抢到CPU资源了

解决方案:当前线程抢到CPU资源后,票的自增和输出执行完毕才能切换到其他线程运行 – 加锁

出现原因
public class MyThread extends Thread{private static int allTicket = 1000;private static int curTicket = 0;public MyThread(String name) {super(name);}@Overridepublic void run() {
//		  举例:
//        curTicket - 0 -1-2-3....while(curTicket < allTicket){
//			线程t1抢到资源,调用run(),curTicket++变成1,还没来得及输出,时间片到了,退出cpu资源
//          线程t3抢到资源,调用run(),curTicket++变成2,还没来得及输出,时间片到了,退出cpu资源
//          线程t2抢到资源,调用run(),curTicket++变成3,输出正在销售第3张,时间片到了,退出cpu资源
//          线程t1抢到资源,输出正在销售第3张票,时间片到了,退出cpu资源
//          线程t3抢到资源,输出正在销售第3张票,时间片到了,退出cpu资源  curTicket++;System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");}}
}
解决方案
public class MyThread extends Thread{private static int allTicket = 1000;private static int curTicket = 0;
//	private Object obj = new Object();第三种new(锁)对象
//	注意:成员变量时,new了三次,三个对象,三把锁,没有锁住
//    	设置成静态的,设置成静态变成一把锁(对象)才锁的住private static Object obj = new Object();public MyThread(String name) {super(name);}@Overridepublic void run() {
//		  举例:
//        curTicket - 0 -1-2-3....while(curTicket < allTicket){//第一种:synchronized (String.class) {任意类的对象在程序中唯一的//第二种:synchronized ("abc") {字符串对象放在常量池也是唯一的synchronized (obj) {//自动上锁//			线程t1抢到资源,调用run(),自动上锁,curTicket++变成1,还没来得及输出,时间片到了,退出cpu资源
//          线程t3抢到资源,调用run(),有锁,退出
//          线程t2抢到资源,调用run(),有锁,退出
//          线程t1抢到资源,输出正在销售第3张票,自动解锁,时间片到了,退出cpu资源
//          线程t3抢到资源...curTicket++;System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");}//自动解锁}}
}
问题三:

多卖了票

出现原因:curTicket到了临界点(999),三个线程都可以进判断,然后上锁

解决方案:在锁中再次判断

出现原因:
public class MyThread extends Thread{private static int allTicket = 1000;private static int curTicket = 0;private static Object obj = new Object();public MyThread(String name) {super(name);}@Overridepublic void run() {
//		  临界值:
//        curTicket - 999while(curTicket < allTicket){synchronized (obj) {//自动上锁//			线程t1抢到资源,调用run(),还没有上锁,挂起
//          线程t3抢到资源,调用run(),还没有上锁,挂起
//          线程t2抢到资源,调用run(),上锁,curTicket++变成1000,输出正在销售第1000张,时间片到了,退出cpu资源
//          线程t1抢到资源,调用run(),上锁,curTicket++变成1001,输出正在销售第1001张,时间片到了,退出cpu资源
//          线程t3抢到资源,调用run(),上锁,curTicket++变成1002,输出正在销售第1002张,时间片到了,退出cpu资源curTicket++;System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");}//自动解锁}}
}
解决方案:
public class MyThread extends Thread{private static int allTicket = 1000;private static int curTicket = 0;private static Object obj = new Object();public MyThread(String name) {super(name);}@Overridepublic void run() {while(curTicket < allTicket){synchronized (obj) {if(curTicket < allTicket){curTicket++;System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");}if(curTicket >= allTicket){//在锁中再次判断System.out.println("窗口" +  Thread.currentThread().getName() + "票已经售完");}}}}
}

注意:

编译字节码命令:javap -verbose MyThread.class(后面会涉及)

监视器开始上锁到解锁
线程安全上锁

锁()new的对象,锁不住是几个对象,需要设置成静态变成一个才锁的住

单线程无锁,多线程会上锁(加的何种锁应情况变化,无锁-偏向锁-轻量级锁-重量级锁-状态标记)

了解内容:感兴趣搜索相关博客等等了解即可

简单来说就是:几个线程就无锁升级到偏向锁,可再变到无锁;有线程数量多了,就会升级到轻量级锁,再多就又升级重量级锁,对于后面加锁就相当给他个互斥状态,注意升级到轻量级锁、重量级锁就降不下去

java对象在内存中的结构,java对象内存分析
简单理解对象内存结构
其中当实例变量达条件,对齐填充就会相应变化
理解对象内存结构

线程安全 – 加锁

注意:要想多个线程互斥住,就必须使用同一把锁(对象)!!!

加锁的方式:

1.synchronized

同步代码块:

synchronized(锁对象){//自动上锁

…想要互斥的代码…

}//自动解锁

2.Lock

总结

1.练习1线程合并(线程类、任务类)

2.买票的案例 – 线程安全 (重要)

3.理解对象内存结构 - https://blog.csdn.net/weixin_44606481/article/details/134802419

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

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

相关文章

FT232RL/FT232RNL替代GP232RNL USB转UART桥接控制器芯片低成本方案

关注过小编的朋友都知道&#xff0c;之前小编有推荐过FT232RL的替代产品GP232RL&#xff0c;软硬件直接兼容&#xff0c;无需做修改。随着产品的更新迭代&#xff0c;后面也出来了升级版GP232RNL&#xff0c;低成本方案&#xff0c;可直接替代FT232RL/FT232RNL&#xff0c;参数…

【数据结构】线性表的定义与基本操作

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;数据结构 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

阿里二面:谈谈ThreadLocal的内存泄漏问题?问麻了。。。。

引言 ThreadLocal在Java多线程编程中扮演着重要的角色&#xff0c;它提供了一种线程局部存储机制&#xff0c;允许每个线程拥有独立的变量副本&#xff0c;从而有效地避免了线程间的数据共享冲突。ThreadLocal的主要用途在于&#xff0c;当需要为每个线程维护一个独立的上下文…

linux之sed编辑器指令练习

目录 一、sed编辑器 二、sed使用案例 1.1 s命令&#xff08;substitute替换&#xff09; 一、sed编辑器 sed编辑器比交互式编辑器快的多&#xff0c;可以简化数据处理任务,sed编辑器并不会修改文件&#xff0c;只会将修改后的数据&#xff0c;输出。 二、sed使用案例 首先…

RK3568平台 iperf3测试网络性能

一.iperf3简介 iperf是一款开源的网络性能测试工具&#xff0c;主要用于测量TCP和UDP带宽性能。它可以在不同的操作系统上运行&#xff0c;包括Windows、Linux、macOS等。iperf具有简单易用、功能强大、高度可配置等特点&#xff0c;广泛应用于网络性能测试、网络故障诊断和网…

【编译tingsboard】出现gradle-maven-plugin:1.0.11:invoke (default)

出现的错误&#xff1a; [ERROR] Failed to execute goal org.thingsboard:gradle-maven-plugin:1.0.11:invoke (default) on project http: Execution default of goal org.thingsboard:gradle-maven-plugin:1.0.11:invoke failed: Plugin org.thingsboard:gradle-maven-plugi…

mysql - 缓存

缓存 InnoDB存储引擎在处理客户端的请求时&#xff0c;当需要访问某个页的数据时&#xff0c;就会把完整的页的数据全部加载到内存中&#xff0c;也就是说即使我们只需要访问一个页的一条记录&#xff0c;那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以…

力扣hot100:207. 课程表

这是一道拓扑排序问题&#xff0c;也可以使用DFS判断图中是否存在环。详情请见&#xff1a;官方的BFS算法请忽略&#xff0c;BFS将问题的实际意义给模糊了&#xff0c;不如用普通拓扑排序思想。 数据结构&#xff1a;图的拓扑排序与关键路径 拓扑排序&#xff1a; class Sol…

交换机高级-端口安全

端口安全 1、一旦接口开启端口安全功能&#xff0c;那么接口所学到的动态MAC就会转换成安全MAC地址&#xff1b; 2、安全MAC地址默认情况下只能学习1个&#xff0c;可以通过命令手动修改学习数量&#xff1b; 3、安全MAC地址没有老化时间&#xff08;但是依然存在内存中&…

2核4g服务器能支持多少人访问?阿里云2核4g服务器在线人数

阿里云2核4G服务器多少钱一年&#xff1f;2核4G配置1个月多少钱&#xff1f;2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

技术周刊 117 期:Visual Copilot、INP、Kimi 支持 200 万字上下文、Grok 开源、Figure 01、Open Sora 开源

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;金骏眉 大家好&#xff0c;我是童欧巴。老规矩&#xff0c;咱们先来看技术资讯。 技术资讯 前端 VitePress (早就应该) 1.0 发布MistCSS&#xff0c;只使用 CSS 来…

聚观早报 | 滴滴2023年Q4营收;微软推广Copilot

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 3月25日消息 滴滴2023年Q4营收 微软推广Copilot 极狐汽车将出口西班牙 华为公开智能驾驶新专利 华为P70系列发布…

Luminar Neo:重塑图像编辑新纪元,Mac与Win双平台畅享创意之旅

在数字时代的浪潮中&#xff0c;图像编辑软件已成为摄影师和设计师们不可或缺的创作工具。Luminar Neo&#xff0c;作为一款专为Mac与Windows双平台打造的图像编辑软件&#xff0c;正以其卓越的性能和创新的编辑功能&#xff0c;引领着图像编辑的新潮流。 Luminar Neo不仅继承…

基于nodejs+vue基于hive旅游数据的分析与应用python-flask-django-php

系统阐述的是使用基于hive旅游数据的分析与应用系统&#xff0c;对于nodejs结构、MySql进行了较为深入的学习与应用。主要针对系统的设计&#xff0c;描述&#xff0c;实现和分析与测试方面来表明开发的过程。开发中使用了express框架和MySql数据库技术搭建系统的整体架构。利用…

Kubernetes概念:服务、负载均衡和联网:2. Gateway API

Gateway API 官方文档&#xff1a;https://kubernetes.io/zh-cn/docs/concepts/services-networking/gateway/ Gateway API 通过使用可扩展的、角色导向的、 协议感知的配置机制来提供网络服务。它是一个附加组件&#xff0c; 包含可提供动态基础设施配置和高级流量路由的 API…

vscode添加gitee

1.创建仓库 2.Git 全局设置 3.初始化仓库 2.1 打开vscode打开需要上传到给git的代码文件 2.2.点击左边菜单第三个的源代码管理->初始化仓库 4.点击加号暂存所有更改 5.添加远程仓库 5.1 添加地址&#xff0c;回车 5.2 填写库名&#xff0c;回车 6.提交和推送 6.1 点击✔提交…

Matlab|基于模型预测控制(MPC)的微电网调度优化的研究

目录 1 主要内容 2 程序难点及问题说明 3 部分程序 4 下载链接 1 主要内容 该程序分为两部分&#xff0c;日前优化部分——该程序首先根据《电力系统云储能研究框架与基础模型》上面方法&#xff0c;根据每个居民的实际需要得到响应储能充放电功率&#xff0c;优化得到整…

【教程】高效数据加密混淆方法及实现简介

背景 在需要对数据进行传输或者表达时&#xff0c;通常要求数据加密的安全级别不高&#xff0c;但希望加解密时间复杂度尽可能低。这时使用传统的对称加密&#xff08;如3DES、AES&#xff09;或非对称加密&#xff08;如RSA、ECC&#xff09;显然不太适合。因为加密的安全级别…

【数据结构】非线性结构——二叉树

文章目录 前言1.树型结构1.1树的概念1.2树的特性1.3树的一些性质1.4树的一些表示形式1.5树的应用2.二叉树 2.1 概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储2.5 二叉树的基本操作 前言 前面我们都是学的线性结构的数据结构&#xff0c;接下来我们就需要来学习非…

WPF---1.入门学习

学习来源 布局 wpf布局原则 一个窗口中只能包含一个元素 不应显示设置元素尺寸 不应使用坐标设置元素的位置 可以嵌套布局容器 StackPanel-->表单条件查找布局 DataGrid wpf布局容器 StackPanel: 水平或垂直排列元素&#xff0c;Orientation属性分别: Horizontal / Vertic…