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

Java线程 - 详解(2)

一,线程安全问题

有些代码在单个线程的环境下运行,完全正确,但是同样的代码,让多个线程去执行,此时就可能出现BUG,这就是所谓的 "线程安全问题"。举一个例子:

public class Demo {static int count = 0;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{for (int i = 0; i < 5000; i++) {count++;}});Thread thread1 = new Thread(()->{for (int i = 0; i < 5000; i++) {count++;}});thread.start();thread1.start();thread.join();thread1.join();System.out.println("count = "+count);}
}

 以上代码如果使用单线程执行,他的答案是10000,但是如果使用多线程,那么就不确定了。

 那么为什么会出现这种情况呢?首先我们要深入了解一下 count++ 这个操作,实际上这个操作是分成 3 步执行的(站在CPU的角度,count++,是有三个指令实现的):

  1. load 把数据从内存读取到 CPU 寄存器中   ——>  tmp = count (简单理解版)
  2. add 把寄存器中的数据进行 +1                   ——>  tmp += 1      (简单理解版)
  3. save 把寄存器中的数据,保存到内存中    ——>  count = tmp  (简单理解版)

在此基础上,又因为线程之间的调度顺序是随机的,就会导致上面的代码出BUG,画一个图来理解一下:

 (上面画的只是一部分情况),也就是说,两个线程分别自增一次,预期得到的是2,实际上可能得到的是1,这就会导致两个线程的结果没有向上累加,而是各自独立运行。

讲了上面的BUG后,还有一个问题,我们得到的count的可能取值范围是多少?是[1,10000],还是[5000,10000]?答案是 [1,10000],因为可能 线程1 自增1次, 而 线程2 自增 n 次,画个图理解一下:

 二,线程安全问题产生原因

1. 操作系统中,线程的调度顺序是随机的(抢占式执行)

        线程的调度顺序是在系统内核实现的,无法解决

2. 两个线程,针对同一个变量进行修改​​​​​​​

        一个线程针对一个变量进行修改 ✔️

        两个线程针对不同变量进行修改 ✔️

        两个线程针对同一个变量进行读取 ✔️

3. 修改操作不是原子的,拿上面的举例就是:count++这个操作不是一步到位的,需要分成4三步执行。

        原子性:将多个操作"封装"起来,使其就相当于一个操作,更加通俗一点,就相当于

        有一个房间,当线程1在该房间执行操作时,线程2要么等待,要么去其他房间执行

4. 内存可见性问题(这章还不涉及)

5. 指令重排序问题(这章还不涉及)

 三,解决线程安全问题

上面讲了5种原因导致线程安全问题,其中1是避免不了的,2是只能在写代码时尽量避免,4,5还没涉及到,因此我们要想解决线程安全问题就只能从原因3入手,即将那些非原子操作转换成原子操作,更加专业一点就是 "加锁"。

在Java中,给代码"加锁"最常见的办法就是使用 synchronized 关键字:

synchronized( ){

        ......

        //要执行的操作放在这里

}

注:( )中需要一个用来加锁的对象。这个对象的类型不重要,重要的是通过这个对象来区分两个线程是否竞争同一个锁,如果两个线程是在竞争同一个锁,就会有锁竞争,如果不是,就不会有锁竞争,就任然是并发执行。

你可以将锁想象成一个单人的自习室,如果你先占用了这个自习室,那么其他人要么阻塞等待你使用结束(锁竞争),要么去其他空的自习室(没有锁竞争)。

public class Demo {static int count = 0;public static void main(String[] args) throws InterruptedException {Object locker = new Object();//锁Thread thread = new Thread(()->{synchronized (locker){for (int i = 0; i < 5000; i++) {count++;}}});Thread thread1 = new Thread(()->{synchronized (locker){for (int i = 0; i < 5000; i++) {count++;}}});thread.start();thread1.start();thread.join();thread1.join();System.out.println("count = "+count);}
}

 在画个图对比一下:

相关文章:

Java线程 - 详解(2)

一&#xff0c;线程安全问题 有些代码在单个线程的环境下运行&#xff0c;完全正确&#xff0c;但是同样的代码&#xff0c;让多个线程去执行&#xff0c;此时就可能出现BUG&#xff0c;这就是所谓的 "线程安全问题"。举一个例子&#xff1a; public class Demo {s…...

事务特性 - 达梦数据库

达梦数据库事务特性 1 事务特性1.1 原子性1.2 一致性1.3 隔离性1.4 持久性 1 事务特性 事务必须具备什么属性才是一个有效的事务呢&#xff1f;一个逻辑工作单元必须表现出四种属性&#xff0c;即原子性、一致性、隔离性和持久性&#xff0c;这样才能成为一个有效的事务。DM 数…...

axios 使用FormData格式发送GET请求

如果你需要使用&#xff0c;FormData格式&#xff0c;发送GET请求 将参数拼接到 FormData对象 中&#xff0c;使用 URLSearchParams 将FormData对象转换为查询参数字符串&#xff0c;并将其拼接到URL中&#xff0c;这样就能以FormData格式发送GET请求给服务器 注意&#xff1…...

CS144(2023 Spring)Lab 1: stitching substrings into a byte stream

文章目录 前言其他笔记相关链接 1. Getting started2. Putting substrings in sequence2.1 需求分析2.2 注意事项2.3 代码实现 3. 测试与优化 前言 这一个Lab主要是实现一个TCP receiver的字符串接收重组部分。 其他笔记 Lab 0: networking warmup Lab 1: stitching substri…...

【PHP】常用的PHP内置函数

1、PHP内置函数非常丰富&#xff0c;用于执行各种任务。以下是一些常用的PHP内置函数&#xff1a; 字符串操作函数&#xff1a; strlen(): 返回字符串的长度。 strpos(): 查找字符串中的某个子串第一次出现的位置。 substr(): 返回字符串的子串。 str_replace(): 替换字符串中的…...

css自学框架之消息弹框

首先我们还是看看消息弹框效果&#xff1a; 主要实现代码分为三部分 一、CSS部分&#xff0c;这部分主要是定义样式&#xff0c;也就是我们看到的外表&#xff0c;主要代码&#xff1a; /* - 弹窗 */notice{top: 0;left: 0;right: 0;z-index: 10;padding: 1em;position: fix…...

42、Flink 的table api与sql之Hive Catalog

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…...

PAT 1145 Hashing - Average Search Time

个人学习记录&#xff0c;代码难免不尽人意。 The task of this problem is simple: insert a sequence of distinct positive integers into a hash table first. Then try to find another sequence of integer keys from the table and output the average search time (the…...

C++调用Python Win10 Miniconda虚拟环境配置

目录 前言1. Win10 安装 Miniconda2. 创建虚拟环境3. 配置C调用python环境4. C调用Python带参函数5.遇到的问题6. 总结 前言 本文记录了Win10 系统下Qt 应用程序调用Python时配置Miniconda虚拟环境的过程及遇到的问题&#xff0c;通过配置Python虚拟环境&#xff0c;简化了Qt应…...

从0到1学会Git(第一部分):Git的下载和初始化配置

1.Git是什么: 首先我们看一下百度百科的介绍:Git&#xff08;读音为/gɪt/&#xff09;是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 …...

【记录】手机QQ和电脑QQ里的emoji种类有什么差异?

版本 手机 QQ&#xff1a;V 8.9.76.12115 电脑 QQ&#xff1a;QQ9.7.15&#xff08;29157&#xff09; 偶然发现&#xff0c;有一种emoji手机上怎么找都找不到&#xff0c;一开始以为自己失忆了&#xff0c;后来发现这种emoji只在电脑上有。 接下来简单说一下找emoji差异的方式…...

blender界面认识01

学习视频 【基础篇】1.2 让手听话_哔哩哔哩_bilibili 目录 控制视角 控制物体 选择对象1 小结 控制视角 长按鼠标中键-----视角旋转 shift鼠标中键-----视角平移 滚动鼠标中键-----视角缩放 也可以通过界面的快捷工具实现 这个视角旋转有一点像catia中罗盘&#xff0c…...

TCP数据报结构分析(面试重点)

在传输层中有UDP和TCP两个重要的协议&#xff0c;下面将针对TCP数据报的结构进行分析 关于UDP数据报的结构分析推荐看UDP数据报结构分析&#xff08;面试重点&#xff09; TCP结构图示 TCP报头结构的分析 一.16位源端口号 源端口表示发送数据时&#xff0c;发送方的端口号&am…...

合并两个有序的单链表,合并之后的链表依然有序

定义节点 class ListNode {var next: ListNode _var x: Int _def this(x: Int) {thisthis.x x}override def toString: String s"x>$x" } 定义方法 class LinkedList {var head new ListNode(0)def getHead(): ListNode this.headdef add(listNode: Li…...

eureka迁移到nacos--双服务中心注册

服务注册中心的迁移有多种方式&#xff0c;官网使用nacos sync&#xff0c;还有民间开发的双注册中心组件eureka-nacos-proxy&#xff0c;但是我用了不太顺利&#xff0c;所以用的是阿里巴巴的双注册中心组件edas-sc-migration-starter spring boot&#xff1a;2.5.3 引入依赖 …...

线程池使用不规范导致线程数大以及@Async的规范使用

文章详细内容来自&#xff1a;线程数突增&#xff01;领导&#xff1a;谁再这么写就滚蛋&#xff01; 下面是看完后文章的&#xff0c;一个总结 线程池的使用不规范&#xff0c;导致程序中线程数不下降&#xff0c;线程数量大。 临时变量的接口&#xff0c;通过下面简单的线…...

启莱OA treelist.aspx SQL注入

子曰&#xff1a;“为政以德&#xff0c;譬如北辰&#xff0c;居其所&#xff0c;而众星共之。” 漏洞复现 访问漏洞url&#xff1a; 使用SQLmap对参数 user 进行注入 漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感…...

ES是一个分布式全文检索框架,隐藏了复杂的处理机制,核心数据分片机制、集群发现、分片负载均衡请求路由

ES是一个分布式框架&#xff0c;隐藏了复杂的处理机制&#xff0c;核心数据分片机制、集群发现、分片负载均衡请求路由。 ES的高可用架构&#xff0c;总体如下图&#xff1a; 说明&#xff1a;本文会以pdf格式持续更新&#xff0c;更多最新尼恩3高pdf笔记&#xff0c;请从下面…...

xml和json互转工具类

分享一个json与xml互转的工具类&#xff0c;非常好用 一、maven依赖 <!-->json 和 xm 互转</!--><dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version></dependency&g…...

Windows系统下MMDeploy预编译包的使用

Windows系统下MMDeploy预编译包的使用 MMDeploy步入v1版本后安装/使用难度大幅下降&#xff0c;这里以部署MMDetection项目的Faster R-CNN模型为例&#xff0c;将PyTorch模型转换为ONNX进而转换为Engine模型&#xff0c;部署到TensorRT后端&#xff0c;实现高效推理&#xff0c…...

Z-Image-GGUF文生图案例分享:看看AI能画出多美的图片

Z-Image-GGUF文生图案例分享&#xff1a;看看AI能画出多美的图片 1. 开篇&#xff1a;当文字遇见画笔 想象一下&#xff0c;你只需要输入一段描述&#xff0c;就能得到一张精美的图片。这不是科幻电影里的场景&#xff0c;而是Z-Image-GGUF带给我们的现实体验。作为阿里巴巴通…...

Leader让我带5个外包,出了问题算我的,绩效好了算团队的,每天当保姆还不如自己写,管理岗这个坑谁爱跳谁跳

看到一哥们吐槽&#xff0c;说leader让他带5个外包&#xff0c;出了问题算他的&#xff0c;绩效好了算团队的&#xff0c;每天当保姆还不如自己写代码。看完我直接笑出声了——不是觉得好笑&#xff0c;是太真实了&#xff0c;笑的是自己也经历过。说实话&#xff0c;这种事在互…...

保姆级教程:用llama.cpp把魔塔社区的safetensors模型转成Ollama能用的GGUF格式

从魔塔社区到Ollama&#xff1a;零基础完成safetensors到GGUF的华丽转身 刚接触开源大模型的新手们&#xff0c;往往会在魔塔社区发现令人心动的模型——比如最近热门的DeepSeek-R1系列。但下载后却面临一个尴尬局面&#xff1a;这些模型通常是safetensors格式&#xff0c;而Ol…...

FUTURE POLICE惊艳效果:毫秒级语音字幕对齐实战演示

FUTURE POLICE惊艳效果&#xff1a;毫秒级语音字幕对齐实战演示 1. 为什么需要精准的字幕对齐&#xff1f; 在视频制作和多媒体处理中&#xff0c;字幕与语音的同步问题一直是个痛点。传统字幕制作往往需要人工逐句校对&#xff0c;耗时耗力。而普通语音识别技术虽然能生成文…...

忍者像素绘卷镜像免配置:内置Prompt语法校验器防无效输入机制

忍者像素绘卷镜像免配置&#xff1a;内置Prompt语法校验器防无效输入机制 1. 产品概述 忍者像素绘卷是一款基于Z-Image-Turbo深度优化的图像生成工作站&#xff0c;专为像素艺术创作而设计。它融合了16-Bit复古游戏美学与现代AI图像生成技术&#xff0c;为用户提供了一个直观…...

MySQL 中 count(*)、count(1) 和 count(字段名) 有什么区别?

一、快速结论&#xff08;先看结论再看分析&#xff09;方式作用效率一句话总结count(*)统计所有行数⭐⭐⭐⭐ 最高我是专业的&#xff01;我为统计而生count(1)统计所有行数⭐⭐⭐⭐ 同样高效我是 count(*) 的马甲兄弟count(列名)统计该列非 NULL 的行数⭐⭐⭐ 较慢我挑剔&…...

Qwen3.5-9B生产环境实践:高并发请求处理+响应延迟优化策略

Qwen3.5-9B生产环境实践&#xff1a;高并发请求处理响应延迟优化策略 1. 项目概述与核心能力 Qwen3.5-9B是一款拥有90亿参数的开源大语言模型&#xff0c;在多个领域展现出卓越的性能。这个模型特别适合需要处理复杂任务的生产环境&#xff0c;因为它具备以下核心能力&#x…...

OpenClaw技能组合:用Qwen2.5-VL-7B+OCR实现全自动发票报销

OpenClaw技能组合&#xff1a;用Qwen2.5-VL-7BOCR实现全自动发票报销 1. 为什么需要自动化发票报销 每次月底整理发票都让我头疼——需要手动截图、识别金额、填写报销单、发送邮件。直到我发现OpenClaw可以通过组合多个技能模块&#xff0c;实现从截图识别到财务审核的全流程…...

MySQL 故障排查与生产环境优化笔记

一、基础信息1. 实验环境数据库版本&#xff1a;MySQL 8.0架构&#xff1a;1 台单实例 2 台主从复制环境用途&#xff1a;模拟生产故障、验证优化方案2. MySQL 逻辑架构&#xff08;四层&#xff09;连接层处理客户端连接、授权认证、权限校验提供线程池、SSL 安全连接服务层S…...

第 6 次执行后,PostgreSQL 执行计划为何突变?

引言 在 PostgreSQL 中&#xff0c;预处理语句通常用于提升性能并防止 SQL 注入。但一个不易察觉的行为是&#xff1a;查询规划器会在执行达到特定次数后自动改变执行计划。 这种变化往往令人困惑——SQL 本身未发生变化&#xff0c;执行计划却突然发生切换&#xff0c;有时甚至…...