4. 多线程(2)---线程的状态和多线程带来的风险

文章目录

  • 前言
  • 1. 线程的状态
    • 1.1. 观察线程的所有状态
    • 1.2. 通过不同线程的状态,来调试代码,观察现象
  • 2. 多线程的带来的风险---线程不安全
    • 2.1.观察线程不安全的现象
    • 2.2 线程不安全的原因
    • 2.3.线程不安全的原因


前言

上一篇博客我们学习了,线程的创建,这次我们讲解 线程的状态 和 线程不安全问题。


1. 线程的状态

1.1. 观察线程的所有状态

从操作系统的视角来看,线程的状态分为:就绪和阻塞。
Java线程也是对操作系统线程的封装,
针对状态这里,Java也进行了重新封装,一共分为下面几种状态

  • NEW:安排了工作,还未开始行动
  • RUNNABLE:可工作的,又可以分成正在工作中和即将开始工作
  • BLOCKED:由于而造成的阻塞
  • WAITING:死等,没有超时时间的阻塞等待
  • TIMED_WAITING:有超时时间的阻塞等待
  • TERMINATED:工作完成了

我们有两种方式观察线程的状态

  1. 使用 getState() 方法
  2. 使用 jconsole 程序观察

下面我们写代码示例,来分别观察线程的状态

  • NEW:安排了工作,还未开始行动
  • TERMINATED:工作完成了
public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{});System.out.println(t.getState());t.start();Thread.sleep(1000);System.out.println(t.getState());}

在这里插入图片描述

  • TIMED_WAITING:有超时时间或者是指定时间的阻塞等待
public class Demo13 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});System.out.println(t.getState());t.start();Thread.sleep(1000);System.out.println(t.getState());}
}

在这里插入图片描述
也可以使用 join(时间) 也会进入到 TIMED_WAITING

public class Demo14 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});System.out.println(t.getState());t.start();t.join(600000*1000);}
}

使用 jconsole 工具
在这里插入图片描述

  • RUNNABLE:可工作的,又可以分成正在工作中和即将开始工作
public class Demo13 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (true){/*try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}*/}});System.out.println(t.getState());t.start();Thread.sleep(1000);System.out.println(t.getState());}
}

while 循环中这段指令虽然什么都没有做,但是一直进行个循环操作,在CPU上执行。

  • WAITING:死等,没有超时时间的阻塞等待
public class Demo14 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});System.out.println(t.getState());t.start();t.join();}
}

下图就是一个简易的不同状态下的转换。
在这里插入图片描述

1.2. 通过不同线程的状态,来调试代码,观察现象

一个程序员工作的大部分时间不是在写代码,而是在调试代码 (找 bug)。
在多次的程序中,理解线程状态,是帮助我们调试程序的关键。
比如,发现某个代码逻辑,好像卡死了 (明明调用了,没有执行/没有执行完)
检查流程:

  1. 使用jconsole 或者其他工具,查看当前的进程中所有的线程,找到对应的逻辑线程是什么
  2. 看线程的状态是什么
    看到 Timed_waiting / waiting,换衣是不是代码在某个方法上产生了阻塞,没有被即使唤醒
    看到 blocked,怀疑是不是代码中出现了死锁
    看到 Runnable,线程本身没有问题,考虑逻辑上某些条件是不是没有触发
  3. 再看看线程具体的调用栈,尤其是在阻塞的状态,现成代码阻塞在哪一行

2. 多线程的带来的风险—线程不安全

2.1.观察线程不安全的现象

下面我们来讨论一下线程不安全的情况。
举例一个例子:
使用两个线程,分别对 count进行加一操作,分别循环5000次,观察结果,预期是 10000次

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

这样写,会不会得到我们想要的结果呢?
在这里插入图片描述
每次结果都不一样,很难出现预期结果,每次发现都不是我们想要的结果。
为什么呢?
这个问题等会再说,
但是我们可以根据上面的代码进行修改,然后得到100000,那怎么进行修改呢?
只需要把start和join进行交换

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

结果如下:
在这里插入图片描述
这样改就可以了竟然,下面我们来讲解一下线程不安全出现的原因。

2.2 线程不安全的原因

我们先解释一下上面出现的原因。
现在就需要我们之前的知识了,站在CPU的角度来看执行指令
在这里插入图片描述
这个操作看起来是一行代码,实际上对应到 3 个CPU 指令

  1. load: 把内存中的值 (count变量) 读取到 CPU 寄存器
  2. add: 把指定寄存器中的值,进行+1操作,结果还是在这个寄存器中
  3. save:把寄存器中的值,写回到内存中

上述这三个指令执行的过程中,CPU随时可能会触发线程的调取切换(因为操作系统的调度是随机的)。
可能会发生下面的情况:
123线程切走
12切走…线程切回来,然后是3
1 线程切走… 线程切回来,然后23一起执行
1线程切走…线程切回来 2线程切走…线程切回来,然后是3线程
下面是图解:
在这里插入图片描述
我们拿出两个来看一下,count的变化
在这里插入图片描述
在这里插入图片描述
上面便是上述代码出现的问题的解释。

2.3.线程不安全的原因

  1. [根本] 操作系统对于线程的调度 是随机的。抢占式的

  2. 多个线程同时修改一个同一个变量
    上述代码t1线程和t2线程修改同一个内存空间
    如果是一个线程修改一个变量 - - - 没有问题
    如果是多个线程,不是同时修改同一个变量 - - - 没有问题
    如果是多个线程修改不同变量 - - - 没问题
    如果多个线程读取同一个变量 - - - 没问题
    取值操作 是 读操作,只有一条执行命令

  3. 修改的操作,不是原子的
    在数据库中我们在讲事务的时候,事务有四大特性:
    原子性 - - - 不可再分,一致性,持久性,隔离性。
    如果修改操作,只是对应一个CPU指令,就可以认为是原子的,CPU不会出现“一条指令执行一半”的情况。
    如果对应到多个CPU指令,那就不是原子的了。

  4. 内存可见性问题,引发的线程不安全

  5. 指令重排序,引发的线程不安全

第4条和第5条等到以后再讲。

关于为什么上面第二个代码可以成功,还有没有别的方法来实现线程安全,请听下回分析!


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

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

相关文章

UE5失真材质

渐变材质函数&#xff1a;RadialGradientExponential&#xff08;指数径向渐变&#xff09; 函数使用 UV 通道 0 来产生径向渐变&#xff0c;同时允许用户调整半径和中心点偏移。 用于控制渐变所在的位置及其涵盖 0-1 空间的程度。 基于 0-1 的渐变中心位置偏移。 源自中心的径…

嵌入式中QT实现文本与线程控制方法

第一:利用QT进行文件读写实现 利用QT进行读写文本的时候进行读写,读取MP3歌词的文本,对这个文件进行读写操作。 实例代码,利用Qfile,对文件进行读写。 //读取对应文件文件,头文件的实现。 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #incl…

计算机毕业设计Python中华古诗词知识图谱可视化 古诗词智能问答系统 古诗词数据分析 古诗词情感分析模型 自然语言处理NLP 机器学习 深度学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

【U8+】用友U8软件中,出入库流水输出excel的时候提示报表输出引擎错误。

【问题现象】 通过天联高级版客户端登录拥有U8后&#xff0c; 将出入库流水输出excel的时候&#xff0c;提示报表输出引擎错误。 进行报表输出时出现错误&#xff0c;错误信息&#xff1a;找不到“fd6eea8b-fb40-4ce4-8ab4-cddbd9462981.htm”。 如果您正试图从最近使用的文件列…

《Spring Framework实战》1:Spring简介

欢迎观看《Spring Framework实战》视频教程 Spring简介 目录 1. Spring简介 2. Spring项目 3. Spring 能做什么&#xff1f; Spring 使 Java 简单化。 Spring 使 Java 现代化。 Spring 使 Java 富有成效。 Spring 使 Java 反应性。 Spring 使 Java 轻松上云。 Sprin…

esp32开发笔记之一:esp32开发环境搭建vscode+ubuntu

最近想用esp32做一个物联网项目&#xff0c;踩坑N个终于有点心得&#xff0c;写下来避免和我一样的小白踩无谓的坑。 写在前面&#xff1a; 第一&#xff0c;大家一定要用linux系统作为编译工具&#xff0c;速度上是windows无法比的&#xff0c;不要因为不熟悉linux而选择win…

Maven 详细配置:Maven 项目 POM 文件解读

Maven 是 Java 开发领域中广泛使用的项目管理和构建工具&#xff0c;通过其核心配置文件——POM&#xff08;Project Object Model&#xff09;文件&#xff0c;开发者能够定义项目的基本信息、依赖关系、插件配置以及构建生命周期等关键要素。POM 文件不仅是 Maven 项目的核心…

低空管控技术-无人机云监视技术详解!

一、无人机监听技术的原理 无人机监听技术主要依赖于射频&#xff08;RF&#xff09;探测、光学和红外传感器等技术手段。这些技术通过被动监听和监测无人机与飞行员&#xff08;或控制器&#xff09;之间的通信链路传输&#xff0c;以确定无人机的位置&#xff0c;甚至在某些…

2024年6月英语六级CET6听力原文与解析

目录 0 序言 1.Long Conversation(长对话) 1.1 Blender 搅拌机 1.2 村庄的改造变化 2.Passage 2.1 micro robots 微型机器人 2.2 elite sleeper 睡眠精英 3.Lecture 3.1 对自身观念变化的低察觉度及相关研究发现 3.2 美国母亲群体数量变化及母亲节消费趋势分析 3.3 …

如何在读博过程中缓解压力

博士生涯充满了挑战和压力&#xff0c;但通过一些实用的方法&#xff0c;我们可以有效地缓解这些压力。以下是我在博士期间采用的一些策略&#xff0c;希望能对正在读博或即将开始博士生涯的你有所帮助。 1. 周末彻底放松 在周末&#xff0c;我尽量避免进行论文写作。这两天…

flutter 专题三十二 Flutter Android embedding升级到v2

一、背景 为了更好地支持将Flutter添加到现有项目的执行环境&#xff0c;旧的Android平台端包装器在 io.flutter.app.FlutterActivity 及其相关类托管Flutter运行时已被弃用。取而代之的则是 io.flutter.embedding.android.FlutterActivity 及其相关的类。如果我们不进行升级&…

MetaGPT - 多Agent框架

文章目录 一、关于 MetaGPT功能介绍快速开始的演示视频教程 二、安装Pip安装Docker安装 一、关于 MetaGPT MetaGPT 为GPTs分配不同的角色&#xff0c;以形成一个协作实体来完成复杂的任务。 github : https://github.com/geekan/MetaGPTtwitter : https://twitter.com/MetaGP…

人工智能伦理困境:技术发展的界限在哪里?

引言 人工智能&#xff08;AI&#xff09;技术正以前所未有的速度改变着世界。从自动驾驶汽车到智能语音助手&#xff0c;再到精准医疗和金融预测&#xff0c;AI正在重塑各行各业。然而&#xff0c;技术进步的背后也隐藏着复杂的伦理难题&#xff0c;引发人们对AI发展边界的深刻…

平安社招 | 平安集团2025年社招笔试平安IQ新胜任力测评个性扫描16PF题库

平安集团旗下专业公司主要包括以保险、银行、资管为代表的综合金融业务和以平安健康、北大国际医院为代表的的医疗健康业务&#xff0c;涵盖金融、医疗、养老的各个领域。中国平安深化“综合金融医疗养老”服务体系&#xff0c;提供专业的“金融顾问、家庭医生、养老管家”服务…

HTML+CSS+JS制作中华传统文化主题网站(内附源码,含5个页面)

一、作品介绍 HTMLCSSJS制作一个中华传统文化主题网站&#xff0c;包含首页、文化艺术页、传统工艺页、文化遗产页、关于我们页等5个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部导航区 包含网站 Logo、主导航菜单&#xff…

深入探讨 Android 中的 AlarmManager:定时任务调度及优化实践

引言 在 Android 开发中&#xff0c;AlarmManager 是一个非常重要的系统服务&#xff0c;用于设置定时任务或者周期性任务。无论是设置一个闹钟&#xff0c;还是定时进行数据同步&#xff0c;AlarmManager 都是不可或缺的工具之一。然而&#xff0c;随着 Android 系统的不断演…

centos服务器 /1ib64/libm.so.6: version “GLIBc 2.27’ not found 异常

centos服务器 /1ib64/libm.so.6: version “GLIBc 2.27’ not found 异常 问题 在服务器使用open3d时&#xff0c;报错缺失GLIBC_2.27&#xff0c;因为后续操作出问题会导致服务器挂&#xff0c;所以最好先备份一下。 解决 查询glibc版本 输入指令查询系统glibc版本&#x…

如何在Windows上编译OpenCV4.7.0

前言 ​ 参考&#xff1a;Win10 下编译 OpenCV 4.7.0详细全过程&#xff0c;包含xfeatures2d 这里在其基础上还出现了一些问题&#xff0c;仅供参考。 正文 一、环境 1、win10 2、cmake-gui 3、opencv4.7.0 4、VS2019 二、编译过程 1、下载需要的文件&#xff1a; 通…

大模型(LLM) 的长上下文与 RAG:评估与回顾

大模型的长上下文与 RAG 以下是本文的主要发现&#xff1a; 在问答基准测试中&#xff0c;LC 的表现通常优于 RAG 基于摘要的检索与 LC 性能相当&#xff0c;而基于块的检索则落后 RAG 在基于对话和一般性问题查询方面具有优势 本文对结果进行了深入分析&#xff0c;请查看。 …

搭建一个本地轻量级且好用的学习TypeScript语言的环境

需求说明 虽然 TypeScript 的在线 Playground 很方便 https://www.tslang.com.cn/play/&#xff0c;但毕竟是在浏览器中使用&#xff0c;没有本地的 IDE 那么顺手。所以我想搭建一个本地类似 Playground 的环境&#xff0c;这样在学习 TypeScript 的过程中&#xff0c;可以更方…