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

【多线程初阶】线程状态 线程安全

文章目录

  • 1.线程状态
    • 线程的状态及状态转移
  • 2.多线程带来的风险 - 线程安全(重点)
    • 线程安全问题产生的原因
    • 如何解决线程安全问题

1.线程状态

EE的第一篇总览中有提到过
进程的状态
1.就绪
2.阻塞
在这里插入图片描述

这都是从操作系统的视角看待的
Java线程也是对操作系统线程的封装,针对状态这里,Java也进行了重新封装,来进行表示

线程的状态及状态转移

在这里插入图片描述

  • NEW : 安排了工作,还未开始行动 -->new 了 Thread 对象,还没 start
    在这里插入图片描述

  • TERMINATED : 工作完成了 -->内核中的线程已经结束了,但是 Thread 对象还在
    在这里插入图片描述

  • RUNNABLE : 可工作的,又可以分成正在工作中或即将开始工作的
    就绪: 1).线程正在CPU上执行 2).线程随时可以去CPU上执行
    比如,前面所举的例子,约A开会,A没有出差,随时可以一起开会

在这里插入图片描述

  • TIMED_WAITING : 这几个都表示排队等着其他事情

①.sleep 状态由RUNNABLE 转变为 TIMED_WAITING
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

②.另外,join(时间)也会进入到TIMED_WAITING状态

指定时间的阻塞,线程阻塞(不参与CPU调度,不继续执行了),阻塞的时间是有上限的

在这里插入图片描述

  • WAITING : 这几个都表示排队等着其他事情

与TIMED_WAITING 的区别,WAITING 会死等,没有超时时间的阻塞等待

在这里插入图片描述
在这里插入图片描述

  • BLOCKED : 这几个都表示排队等着其他事情

也是一种阻塞,比较特殊,由于 锁 导致的阻塞

这个线程状态等我们介绍到线程安全问题 涉及到死锁再进行演示

多线程的程序中,理解线程状态,是帮助我们调试程序的关键

比如,发现代码中某个逻辑,好像卡死了(明明调用了,却没有执行/没有执行完)
1.jconsole / 其他工具,查看当前的进程中的所有线程,找到你对应逻辑的线程是谁
2.看线程的状态是啥
看到 TIMED_WAITING / WAITING ,怀疑是不是代码中某个方法产生阻塞,没有被及时唤醒
看到 BLOCKED ,怀疑是不是代码中出现死锁
看到 RUNNABLE ,线程本身没问题,考虑逻辑上某些条件没有预期触发之类的
3.再看看线程具体的调用栈(尤其是 阻塞的状态,线程的代码阻塞在哪一行了…)

2.多线程带来的风险 - 线程安全(重点)

想给出一个线程安全的确切定义是复杂的,但我们可以这样认为:如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的

如果不理解线程安全问题,是很难保证写出正确的多线程代码的

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
再回到我们观察到的现象,多次执行的结果都不一样并且没有达到预期结果

这样的代码很明显就是有BUG
实际执行效果和预期效果不符合,就叫bug
这样的问题,多线程并发执行引起的问题
如果把两个线程,变成串行执行(一个结束,再执行另一个)

我们将代码写成纯串行执行,观察现象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
CPU内部包含寄存器这样的模块,寄存器也能存一些数据
在这里插入图片描述
我们使用时间轴(先执行的画上面,后执行的画下面)来模拟几个随机调度的顺序,我们只画几种情况,肯定不止这些,因为调度次序存在无数种可能

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

第一种情况的具体分析

在这里插入图片描述
第三种情况的具体分析

在这里插入图片描述
其他的情况就不一一进行分析了
通过上述讨论,不难发现,如果两个线程load时出现的数据都是0,那么意味着一定会少加一次,如果一个load到0另一个load到1,结果就是正确的,也就是说一个线程load需要再另一个线程的save之后才是正确的,所以我们上述的6种随机调度情况只有前两种是正确的

那么上述代码运行出来一定是 >=5w的吗?是否有可能<5w?
是有可能的,只不过概率很小
可以再用随机调度的情况分析一下,是否存在这种情况
在这里插入图片描述

是有可能的,只不过概率要小很多

在这里插入图片描述
在这里插入图片描述

50次和5000次甚至5w次,线程执行的时间长短是不同的
如果是循环50次,很可能在t2.start开始之前,t1 就算完了等后续t2再执行,虽然代码写的是并行,但变成纯串行了

线程安全问题产生的原因

在这里插入图片描述
本篇文章先讨论前三种原因

  • 1.[根本原因] 操作系统对于线程的调度是随机的,抢占式执行
  • .多个线程同时对同一变量进行修改
    在这里插入图片描述

如果是一个线程修改一个变量 -->没问题
如果是多个线程,不是同时修改同一个变量 -->没问题
如果多个线程修改不同变量 -->没问题
如果多个线程读取同一变量 -->没问题
这些都不会出现中间结果相互覆盖的情况
其中修改操作->写,取值操作->读

  • 3.修改操作,不是原子的

原子性,在数据库-事务的学习中,有提到
事务具有1.原子性 2.一致性 3. 持久性 4.隔离性
其中这个原子性,如果是修改操作.只是对应到一个CPU指令,就可以认为是原子的 ,CPU不会出现"一条指令执行一半"的情况,但是count++ 这行代码对应三条CPU指令,就不是原子的

像是++,–,+=,-=都不是原子的,像 = 赋值操作在Java就是原子的

  • 4.内存可见性问题,引起的线程不安全
  • 5.指令重排序引起的线程不安全

最后两个问题,我们后续再讨论~~

如何解决线程安全问题

  • 1.[根本问题]操作系统对于线程的调度是随机的,抢占式执行

这是操作系统的底层设定,我们左右不了

  • 2.多个线程同时对同一变量进行修改

这个和代码的结构相关,调整代码结构,规避一些线程不安全的代码,但是这样的方案不够通用,有些情况下,需求上就是需要多线程修改同一变量,比如超买/超卖的问题:某个商品,库存100件,能否创建101个订单?

  • 3.修改操作,不是原子的

Java中解决线程安全问题的最主要方案:加锁

计算机中的锁,和生活中的锁,是同样的概念,互斥/排他的
把锁"锁上" 称为 “加锁”
把锁"解开" 称为 “解锁”
一旦把锁加上了,其他人要想加锁,必须要阻塞等待

就可以使用锁,把刚才不是原子操作的 count++ 包裹起来,在count++ 之前,先加锁,然后进行 count++,计算完毕后,在解锁,也就是在执行三条CPU指令过程中,其他线程就没法插队了

加锁操作,不是禁止这个线程被调度走,而是禁止其他线程重新加这个锁,避免其他线程的操作在当前线程执行过程中,插队

加锁/解锁 本身是 操作系统提供的API,很多编程语言都对于这样的API进行了封装,大多数的封装风格,都是采取lock()加锁,unlock()解锁 这两个函数

在Java中使用 synchronized 这样的关键字,搭配代码块,来实现类似的效果

  • 进入 synchronized 修饰的代码块,相当于加锁
  • 退出 synchronized 修饰的代码块,相当于解锁
http://www.xdnf.cn/news/10037.html

相关文章:

  • 进阶智能体实战九、图文需求分析助手(ChatGpt多模态版)(帮你生成 模块划分+页面+表设计、状态机、工作流、ER模型)
  • Ubuntu 安装 FSL 及多模态脑MRI的去颅骨处理(含 HD-BET 深度学习方法)
  • 区域未停留检测算法AI智能分析网关V4打造铁道/工厂/机场等场景应用方案
  • mysql隐式转换会造成索引失效的原因
  • 软件评测机构如何保障质量?检测资质、技术实力缺一不可
  • 历年浙江大学计算机保研上机真题
  • JavaScript 性能优化实战研讨
  • antDesignVue中a-upload上传组件的使用
  • Ubuntu开机自动运行Docker容器中的Qt UI程序
  • redis持久化策略
  • ansible自动化playbook简单实践
  • 从监控到告警:Prometheus+Grafana+Alertmanager+告警通知服务全链路落地实践
  • 湖北理元理律师事务所:债务优化中的生活保障实践
  • Java—— 多线程 第二期
  • 新松机械臂 2001端口服务的客户端例程
  • UI自动化测试中的元素等待机制解析
  • 山海鲸轻 3D 渲染技术深度解析:预渲染如何突破多终端性能瓶颈
  • 【Netty系列】核心概念
  • 【Unity博客节选】Playable系统 UML类图与结构分析
  • window10下docker方式安装dify步骤
  • 动态IP与区块链:重构网络信任的底层革命
  • RK3399 Android7.1增加应用安装白名单机制
  • Android 开发 Kotlin 全局大喇叭与广播机制
  • 2025 年 Solana 生态全景分析:它如何从以太坊「高速替代方案」成长为成熟的基础设施?
  • [CSS3]响应式布局
  • 多卡训练核心技术详解
  • TreeMap、TreeSet和HashMap、HashSet
  • PCB设计实践(三十一)PCB设计中机械孔的合理设计与应用指南
  • 【Java学习笔记】接口
  • 解决开发者技能差距:AI 在提升效率与技能培养中的作用