Java--多线程基础知识(2)

一.多线程的中断

1.通过自定义的变量来作为标志位

import java.util.Scanner;public class Demo1 {public static boolean flg = false;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (!flg){System.out.println("t1线程正在执行");try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("t1线程被中断");}}System.out.println("t1线程停止");});t1.start();Scanner scanner = new Scanner(System.in);scanner.next();flg = true;}
}

在代码中,我们将 flg 作为标志位,当代码运行的时候我们需要输入任意键为就可以改变 flg,以此来达到停止代码运行的作用;

注意:flg 必须作为全局变量,因为 lambda 捕获的局部变量是不能被修改的,这是为了确保代码的安全性和一致性,由于我们要对flg 进行修改,所以我们要将 flg 作为全局变量。

2.使用 Thread.interrupted() 或者

Thread.currentThread.isInterrupted()代替标志位

import java.util.Scanner;public class Demo2 {public static void main(String[] args) {Thread t1 = new Thread(()->{Thread cur = Thread.currentThread();while (!cur.isInterrupted()){System.out.println("t1线程正在执行");try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("t1线程被中断");}}System.out.println("t1线程停止");});t1.start();Scanner scanner = new Scanner(System.in);scanner.next();t1.interrupt();}
}

以上代码我们将 cur.isInterrupted 作为标志位,当我们输入任意字符之后就会调用 t1.interrupt() 方法,将标志位改为 false,但是运行后并且输入后我们发现代码仍在执行,

Java的线程中断机制有这样的设计:

当一个线程在阻塞状态(Thread.join,Thread.sleep,Thread.wait)下被中断时,中断标志位会被自动清除,也就是说当调用 Thread.interrupt() 之后,虽然  !cur.isInterrupted() 改为了 false,但是由于被清除了标志位,又变为了 true,这才导致程序仍然在运行。

也就是说 interrupt 方法做了两件事:首先将标志位设置为了 true,但是由于唤醒了 sleep ,以异常的方式返回了,清除了线程的标志位,将标志位重新设置为了 false。针对这种情况,我们需要在 catch 中做更详细的处理。

二.线程的等待与安全问题

public class Demo3 {static int result = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});t1.start();t2.start();Thread.sleep(1000);System.out.println( result);}
}

以上代码我们创建了两个线程 : 分别是 t1,t2,这两个线程同时对 result ++,正常情况下,result 的结果肯定是100000的,但在我们运行代码后:

我们发现并没有到达100000,这就是多线程引发的线程安全问题,线程对 result++ 要进行三个操作:首先将 result 从主内存中读取到工作内存中,其次将 result 在工作内存中++,最后将result的值放回到主内存中,问题就出在这几步上,由于操作不是原子的,导致在 t1 从主内存中读取到 result的值并且++之后,t2也进行了从主内存中读取到了 result 的值,此时t1 和t2都需要将工作内存中的值写入到主内存当中,这样就会导致其中一个线程的值会覆盖掉另一个线程的值,这才导致了 result 的值并没有到达100000;

针对这个问题:

我们可以让某一个线程通过 Thread.sleep() 来等待另一个线程运行,但这种方法我们无法确定另一个线程运行多久,所以不推荐使用。

第二个方法是运行 join() 方法,

public class Demo3 {static int result = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});t1.start();t1.join();t2.start();Thread.sleep(1000);System.out.println( result);}
}

在 t2 运行之前调用 t1.join() 方法,此时main线程会等待 t1 线程运行完成之后再往下走调用 t2.start,此时就解决了上述问题。

三.线程的状态

线程的状态分为六种:

new,runnable,terminated 状态演示:

public class Demo4 {public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0; i < 100; i++) {}});System.out.println(t1.getState());t1.start();System.out.println(t1.getState());try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t1.getState());}}

timed_waiting , blocked 状态演示:

public class Demo5{static Object object = new Object();public static void main(String[] args) {Thread t1 = new Thread(()->{synchronized( object){try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 = new Thread(()->{synchronized ( object){while ( true){}}});t1.start();t2.start();try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t1.getState());System.out.println(t2.getState());}
}

将上述部分代码进行更改,就可以得到 waiting 状态:

        Thread t1 = new Thread(()->{synchronized( object){try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}});

四.线程的锁

还是对上述的线程安全问题进行解决,线程安全问题其中的导火索之一便是代码的非原子性,由于一件事情要分几步来操作,这种情况容易出问题,所以就出现了锁,锁可以将几个步骤的操作绑定在一起,这样就可以变相的实现代码的原子化。

public class Demo6 {static int result = 0;static Object locker = new Object();public static void add(int a){synchronized (locker){result++;}}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {add(result);}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {add(result);}});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

sychronized(Object object){ }

其中 sychronized 就是锁的关键字,他需要的参数是 Object 类,就像是对 object 上了锁,其他人想要进是没有办法的,除非你释放了锁,当你走完{ }的时候,此时就完成了对锁的释放。

上述代码我们对 add类里面的内容上了锁,我们还可以对线程的内容直接加锁:

public class Demo6 {static int result = 0;static Object locker = new Object();public static void add(int a){result++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}}});Thread t2 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}};});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

运行结果不变;

要注意:我们不能只对一个线程加锁,那样的话是没有意义的:

public class Demo6 {static int result = 0;static Object locker = new Object();public static void add(int a){result++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {add(result);};});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

这是因为如果我们只对一个线程的内容上锁了,另一个线程没有对同一个Object类上锁,两个线程还是会各自运行各自的。

最后就是:两个线程针对不同的 Object 类上锁也会有线程安全问题:

public class Demo6 {static int result = 0;static Object locker = new Object();static Object locker2 = new Object();public static void add(int a){result++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}}});Thread t2 = new Thread(()->{synchronized(locker2){for (int i = 0; i < 50000; i++) {add(result);};}});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

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

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

相关文章

Qit_计网笔记

第1章 概述1.1 计算机网络在信息时代中的作用一、计算机网络基础概念&#xff08;一&#xff09;计算机网络的定义定义&#xff1a;计算机网络在信息时代中起到核心作用&#xff0c;实现了万物联网和人人用网的目标。&#xff08;二&#xff09;计算机网络的特点信息时代特征&a…

【C++11】initializer_list列表初始化、右值引用和移动语义、可变参数模版等

目录 前言 一、简介一下C11 二、{}列表初始化 三、右值引用和移动语义 四、右值引用和移动语义的使用场景 五、右值引用和移动语义在传参中的提效 六、引用折叠和完美转发 七、可变参数模板 前言 本文主要介绍C11中新增的一些重要语法&#xff1a;包括initializer_list列表初…

MP3 ID3标签中的数字流派代码和文本值翻译成的中文列表

将MP3 ID3标签中的数字流派代码和文本值翻译成的中文列表&#xff1a;■ 数字代码流派:0 布鲁斯 (Blues)1 古典摇滚 (Classic Rock)2 乡村音乐 (Country)3 舞曲 (Dance)4 迪斯科 (Disco)5 放克 (Funk)6 垃圾摇滚 (Grunge)7 嘻哈 (Hip-Hop)8 爵士乐 (Jazz)9 金属乐 (M…

U8g2库为XFP1116-07AY(128x64 OLED)实现菜单功能[ep:esp8266]

使用U8g2库为XFP1116-07AY&#xff08;128x64 OLED&#xff09;实现菜单功能&#xff0c;核心是通过按键控制菜单切换、光标移动和选项选中&#xff0c;结合U8g2的绘图/文本函数实现交互逻辑支持多级菜单&#xff08;主菜单→子菜单→功能执行&#xff09;&#xff0c;并兼容ES…

easy-dataset 框架综合技术分析:面向领域特定 LLM 指令数据的合成

摘要 本报告对 easy-dataset 框架 进行全面技术剖析&#xff0c;该框架旨在解决大型语言模型&#xff08;LLM&#xff09;在特定领域应用中的核心瓶颈——高质量指令微调数据的稀缺性。随着 LLM 技术发展&#xff0c;其应用能力不再仅依赖模型参数规模&#xff0c;而是更依赖通…

【开题答辩全过程】以 4s店汽车销售系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

测试中的Bug

文章目录软件测试的生命周期软件测试的各个阶段线上环境测试中的BUG描述测试BUGBUG的级别为啥要定义BUG的级别&#xff1f;BUG有哪些级别呢&#xff1f;BUG的生命周期测试与开发发生争执怎么办&#xff1f;测试与开发会发生啥争执&#xff1f;为啥会发生这样的争执&#xff1f;…

aws共享一个镜像并有画图功能

这样可以方便的把系统安装好&#xff0c;不会重复劳动了。 这个是frequi 单独安装 wget https://github.com/freqtrade/frequi/releases/download/2.0.7/freqUI.zip freqtrade install-ui pip install -U -r requirements-plot.txt 在AWS上把已经安装好的环境共享给其他用户。…

C语言---goto语句

文章目录基本语法代码示例goto 的常见用途&#xff08;尽管不推荐&#xff09;为什么 goto 声名狼藉&#xff1f;&#xff08;goto的缺点&#xff09;如何避免使用 goto&#xff1f;&#xff08;替代方案&#xff09;goto 语句是一种无条件跳转语句&#xff0c;它用于将程序的控…

Flask框架的简单了解

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录1. 前言2. 简介3. 核心特点4. 代码实例5. 主要…

——贪心算法——

目录 1 柠檬水找零 2 将数组和减半的最少操作次数 3 最大数 4 摆动序列 5 最长递增子序列 6 递增的三元子序列 7 最长连续递增序列 8 买卖股票的最佳时机 9 买卖股票的最佳时机 II 10 K 次取反后最大化的数组和 11 按身高排序 12 优势洗牌 13 最长回文串 14 增减…

网络操作系统与分布式操作系统的区别

网络操作系统与分布式操作系统的区别架构设计网络操作系统&#xff08;NOS&#xff09;基于客户端-服务器模型&#xff0c;通过共享资源&#xff08;如文件、打印机&#xff09;提供服务&#xff0c;各节点保留独立的管理和数据处理能力。分布式操作系统&#xff08;DOS&#x…

RabbitMQ—运维篇

RabbitMQ安装 RabbitMQ需要依赖erlang&#xff0c;如果普通安装需要安装erlang并保证二者兼容&#xff0c;因此选择较为简单的docker安装方式 1.获取rabbitmq镜像 docker pull rabbitmq:3.11.19-management #rabbitmq-management表示带有客户端&#xff08;控制台&#xff09; …

【学习K230-例程21】GT6700-UDP-Client

B站视频 UDP 简介 UDP 是 User Datagram Protocol 的简称&#xff0c;中文名是用户数据报协议&#xff0c;是 OSI&#xff08;Open SystemInterconnection&#xff0c;开放式系统互联&#xff09;参考模型中一种无连接的传输层协议&#xff0c;提供面向事务的简单不可靠信息传送…

LazyLLM教程 | 第9讲:微调实践:让大模型和向量模型更懂你的领域

前面教程中&#xff0c;我们通过优化检索策略、召回重排略以及基于大模型的查询重写策略来提升了RAG系统的检索精度&#xff0c;但最终回复的结果还需要经过大模型的融合和处理&#xff0c;模型能力的强弱直接影响到最终的结果。这就好比一道好的菜不仅需要有高质量的食材&…

六、vue3后台项目系列——页面自适应设计+pinia,vuex的使用

前言&#xff1a;在页面加入自适应是提高用户体验的一种形式&#xff0c;甚至有时候是手机用户&#xff0c;我们就需要做一个自适应处理&#xff0c;其中肯定会涉及一些状态条件的判断&#xff0c;而这些关键的条件就是我们用来切换样式的关键&#xff0c;所以我们需要使用状态…

视频讲解|Python用ResNet残差神经网络在大脑出血CT图像描数据预测应用

全文链接&#xff1a;https://tecdat.cn/?p43843 原文出处&#xff1a;拓端抖音号拓端tecdat 分析师&#xff1a;Zikun Zhang 视频讲解Python用ResNet残差神经网络在大脑出血CT图像描数据预测在临床医疗影像诊断中&#xff0c;大脑出血的快速准确识别直接关系到患者的救治效率…

Mysql中有那些锁

按照锁的力度分&#xff1a;1.行级锁2.表级锁3.全局锁4.页级锁innodb不支持页锁全局锁全局锁指的是对整个数据库实例加锁&#xff0c;一般用于数据库的表级锁表锁 是对整张表进行加锁。表级锁还有以下几种&#xff1a;意向锁&#xff1a;意向锁是指&#xff0c;我们在事务请求表…

基于 CoT 思维链协调多 MCP 工具:依托亚马逊云科技服务打造全流程智能的 Amazon Redshift 运维体系

基于 CoT 思维链协调多 MCP 工具&#xff1a;依托亚马逊云科技服务打造全流程智能的 Amazon Redshift 运维体系 新用户可获得高达 200 美元的服务抵扣金 亚马逊云科技新用户可以免费使用亚马逊云科技免费套餐&#xff08;Amazon Free Tier&#xff09;。注册即可获得 100 美元的…

手机群控平台的智能管控技术深度解析

手机群控平台作为数字化运营的核心工具&#xff0c;正在重塑移动设备管理的技术边界。其核心价值在于通过集中化控制实现批量化操作&#xff0c;同时借助智能化算法提升管控效率。本文将深入探讨其技术架构与实现方案。平台架构与核心技术手机群控平台采用分布式架构设计&#…