多线程合并练习题,线程安全(售票任务引入)--学习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;

	@Override
	public 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;

	@Override
	public 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;

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public void run() {
//		  临界值:
//        curTicket - 999
		while(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);
	}

	@Override
	public 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Open CASCADE 用户指南:可视化

文章目录 介绍基本概念呈现(Presentation)Presentation 的结构Presentation 包(package)基本示例&#xff1a;如何显示 3D 对象 选择(Selection)术语和概念算法包和类(Packages and classes)使用示例 应用交互服务&#xff08;AIS&#xff09;介绍交互对象呈现(Presentations)隐…

【C语言】【Leetcode】88. 合并两个有序数组

文章目录 一、题目二、思路再思考 一、题目 链接: link 二、思路 这题属于简单题&#xff0c;比较粗暴的做法就是直接比较两个数组&#xff0c;先把第二个数组加到第一个的后面&#xff0c;如何冒泡排序&#xff0c;这种方法简单粗暴但有效&#xff0c;可是不适用于这题&…

Vue3使用v-if指令进行条件渲染

Vue3使用v-if指令进行条件渲染 条件渲染是根据条件的真假来有条件地渲染元素。在Vue.js 3.x中&#xff0c;常见的条件渲染包括使用v-if指令和v-show指令。本文讲解使用v-if指令进行条件渲染。 2.3.1 v-if/v-else-if/v-else 在Vue中使用v-if、v-else-if和v-else指令实现条件…

数据结构·二叉树(1)

目录 1 树的概念及结构 1.1 树的结构 1.2 树的概念 1.3树的表示 2 二叉树的概念及结构 2.1二叉树的概念 2.2 特殊的二叉树 2.3 二叉树的存储结构 1 树的概念及结构 1.1 树的结构 前面所学到的顺序表链表等&#xff0c;都是线性的数据结构&#xff0c;今天介绍的树&am…

第九届蓝桥杯大赛个人赛省赛(软件类)真题C 语言 A 组-第几个幸运数字

幸运数字是可以被3,5,7任一整除的数字&#xff0c;列举小明号码内的所有可能组合并计数。注意别忘了把1占的一位减去。 #include<stdio.h> typedef long long ll; int main(){long long ans 0, n 59084709587505LL;for(ll i 1; i < n; i * 3){//计算小于等于n的数…

代码随想录训练营Day32:● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 题目链接 https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/ 题目描述 思路 看完视频讲解之后豁然开朗啊简直了&#xff01;&#xff01;&#xff01; 统计后一天减去前一天&#xff0c;差值为正数的&#xff0c;再…

一篇文章带你搞定接单的多种渠道,赶紧码住!!!

相信大家也看到了不少人通过网络接单实现年入30W&#xff0c;彻底财富自由&#xff01;咱看了&#xff0c;真的很难不心痒痒&#xff0c;想加入其中&#xff0c;大干一场&#xff01;毕竟搞钱嘛&#xff01;才是王道。但是呢&#xff0c;也有很多人心向往之&#xff0c;奈何不知…

C++剑指offer与高频面试题源码解答与分析

这是博主在当初秋招刷题时候记录的剑指offer第二版以及一些高频题的C源码和解法分析&#xff0c;可以说把这上面的题练好了面试不虚&#xff0c;最后也顺利帮助我拿下baidu ali meituan等多家大厂offer。整篇文章写了大概5W个字&#xff0c;也是积累了很长一段时间的作品&#…

MYSQL通过substr函数与instr函数截取字符串

mysql通过substr函数与instr函数截取字符串 1.从字段左边开始第三位&#xff0c;截取到结束2.截取字段前三位3.截取字段后三位4.从字段右边开始第三位&#xff0c;往后截取一位5.从字段小数点的位置开始&#xff0c;向后截取两位&#xff08;使用了instr&#xff08;&#xff0…

leetcode刷题日记-外观数组

题目描述 解题思路 初始化字符串 init 为 “1”&#xff0c;作为外观数列的第一项。 通过循环迭代生成外观数列的下一项&#xff0c;循环次数为 n-1&#xff0c;因为已经初始化了第一项。 在每次迭代中&#xff0c;通过两个指针 pos 和 start 来遍历当前项 init&#xff0c;po…

22.保护性暂停扩展(一对一)

如果需要多个类之间使用GuardedObject对象&#xff0c;作为参数传递不是很方便&#xff0c;因此设计一个解耦的中间类&#xff0c;这样不仅能够解耦结果的等待者和结果生产者&#xff0c;还能够支持多个任务的管理。 Futures就好比居民楼一层的信箱&#xff0c;每个信箱有房间的…

大数据面试题 —— Flume

目录 介绍 FlumeFlume 架构请说一下你提到的几种 source 的不同点Flume 传输数据时如何保证数据一致性TailDir 为什么可以断点重传说下Flume事务机制Sink 消费能力弱&#xff0c;Channel 会不会丢失数据数千个Flume要怎么统一配置&#xff0c;修改就分发吗Flume一个节点宕机了怎…

【科研基础】分布式信源编码与中继通信

[1] Bian, Chenghong, et al. “Deep joint source-channel coding over cooperative relay networks.” arXiv preprint arXiv:2211.06705 (2022). [2] Bian, Chenghong, et al. “Process-and-Forward: Deep Joint Source-Channel Coding Over Cooperative Relay Networks.”…

还在用传统知识库?AI知识库才是企业的最优选择

在数字化和信息化日趋严重的时代&#xff0c;企业不仅要处理海量的数据&#xff0c;同时还要有效地管理和利用它们。这就使得知识库&#xff0c;作为一种集中存储、管理和共享知识资源的工具&#xff0c;被越来越多的企业所重视。然而&#xff0c;随着技术的快速迭代&#xff0…

RabbitMQ 安装保姆级教程

目录 1.MQ引言 1.1 什么是MQ 1.2 MQ有哪些 1.3 不同MQ特点 2.RabbitMQ 的引言 2.1 RabbitMQ 2.2 RabbitMQ 的安装 2.2.1 下载 2.2.2 下载的安装包 2.2.3 安装步骤 3. RabiitMQ 配置 3.1RabbitMQ 管理命令行 3.2 web管理界面介绍 3.2.1 overview概览 3.2.2 Admin用…

蓝桥杯物联网遇见的重大BUG及其产生原因和解决方法

BUG列表 1、ADC的RP2显示一直为0&#xff1a;2、LORX_Tx发送数据乱码&#xff1a;3、strcmp比较char a[2] {1, 2}与“12”字符串是否相等板子会死机&#xff1a;4、LORA_Tx和LORA_Rx放一起会接收不到数据&#xff1a;5、RTC获取到静止时间&#xff1a;6、ADC获取RP1和RP2模拟量…

基于java+springboot+vue实现的图书借阅系统(文末源码+Lw+ppt)23-328

摘 要 伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对系统进行规范而严格是十分有必要的&#xff0c;所以许许多多的信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套“期待相遇”图书借阅系统&#xff0c;帮助商…

citus的快速开始

准备 dockercitus最新版本&#xff08;docker pull citusdata/citus&#xff09; docker网络 docker network create --subnet172.72.9.0/24 citus-test docker network ls启动citus服务 启动协调节点 docker run -dit --name citus-cod -p 5433:5432 -e POSTGRES_PASSWOR…

背景减除(1)--bgslibrary Windows编译和使用

入侵监控领域中&#xff0c;在固定场景下&#xff0c;需要检测和监控的入侵物体种类繁多&#xff0c;无法具体穷尽。传统的CV算法提取的特征应用场景有限&#xff0c;无法完成大量物体的监控&#xff1b;深度学习目标检测方法没法收集到无穷无尽的物体种类&#xff0c;因此监督…