【JavaEE初阶】线程安全问题及解决方法
目录
一、多线程带来的风险-线程安全
1、观察线程不安全
2、线程安全的概念
3、线程不安全的原因
4、解决之前的线程不安全问题
5、synchronized 关键字 - 监视器锁 monitor lock
5.1 synchronized 的特性
5.2 synchronized 使用示例
5.3 Java 标准库中的线程安全类
一、多线程带来的风险-线程安全
1、观察线程不安全
public class ThreadDemo2 {private static long count = 0;public static void main(String[] args) throws InterruptedException{Thread t1 = new Thread(()->{for (int i = 1;i <= 500000;i++) {count++;}});Thread t2 = new Thread(()->{for (long i = 0;i < 500000;i++) {count++;}});t1.start();t2.start();t1.join();t2.join();//存在线程安全问题,输出的结果可能不准确System.out.println("count= "+count);}
}
其运行结果:

明显这个结果和我们的预期是不一样的,这是因为存在线程安全问题。若把count++的操作在一个单线程环境下运行 ,便不会出现这样的问题。下面我们来说一下线程安全问题。
2、线程安全的概念
如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。线程安全,在单线程环境下和多线程环境下都不会出现问题。
3、线程不安全的原因
- 线程调度是随机的
这是线程安全问题的根本原因 ;随机调度使⼀个程序在多线程环境下,执行顺序存在很多的变数;程序猿必须保证 在任意执行顺序下 , 代码都能正常工作。
- 修改共享数据
多个线程修改同⼀个变量。

- 原子性

什么是原子性:
我们把⼀段代码想象成⼀个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。
那我们应该如何解决这个问题呢?是不是只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。有时也把这个现象叫做同步互斥,表示操作是互相排斥的。
⼀条 java 语句不⼀定是原子的,也不一定只是一条指令。
- 从内存把数据读到 CPU 寄存器中
- 进行数据更新
- 把数据写回到内存
那么不保证原子性会给多线程带来什么问题呢?
如果不保证原子性,⼀个线程正在对⼀个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。这点也和线程的抢占式调度密切相关,如果线程不是 "抢占" 的,就算没有原子性,问题也不⼤。
- 可见性
可见性指,⼀个线程对共享变量值的修改,能够及时地被其他线程看到。这里先不过多介绍。
- 指令重排序
- 去前台取下 U 盘
- 去教室写 10 分钟作业
- 去前台取下快递
编译器对于指令重排序的前提是 "保持逻辑不发⽣变化". 这⼀点在单线程环境下比较容易判断, 但是在多线程环境下就没那么容易了, 多线程的代码执行复杂程度更高, 编译器很难在编译阶段对代码的执行效果进行预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价.
重排序是⼀个比较复杂的话题, 涉及到 CPU 以及编译器的⼀些底层⼯作原理, 此处不做过多讨论。
4、解决之前的线程不安全问题
解决之后的代码:
public class ThreadDemo2 {private static long count = 0;public static void main(String[] args) throws InterruptedException{Object locker = new Object();Thread t1 = new Thread(()->{for (int i = 1;i <= 500000;i++) {synchronized (locker) {count++;}}});Thread t2 = new Thread(()->{for (long i = 0;i < 500000;i++) {synchronized (locker) {count++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println("count= "+count);}
}
这时的结果就一定是1000000,如图:

下面就给大家解释一下,这个线程不安全的问题是如何解决的。
5、synchronized 关键字 - 监视器锁 monitor lock
5.1 synchronized 的特性
1) 互斥
- 进入 synchronized 修饰的代码块,相当于 加锁
- 退出 synchronized 修饰的代码块,相当于 解锁


可以粗略理解成, 每个对象在内存中存储的时候, 都存有⼀块内存表示当前的 "锁定" 状态(类似于厕所 的 "有人/无人").如果当前是 "无人" 状态, 那么就可以使用, 使用时需要设为 "有人" 状态.如果当前是 "有人" 状态, 那么其他人无法使用, 只能排队

理解 "阻塞等待":
针对每⼀把锁, 操作系统内部都维护了⼀个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝试 进行加锁, 就加不上了, 就会阻塞等待, ⼀直等到之前的线程解锁之后, 由操作系统唤醒⼀个新的线程, 再来获取到这个锁。注意:
- 上⼀个线程解锁之后, 下⼀个线程并不是立即就能获取到锁. 而是要靠操作系统来 "唤醒". 这也就是操作系统线程调度的⼀部分工作.
- 假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不⼀定就能获取到锁, 而是和 C 重新竞争, 并不遵守先来后到的规则.
synchronized的底层是使用操作系统的mutex lock实现的。
2) 可重入
synchronized 同步块对同⼀条线程来说是可重入的,不会出现自己把自己锁死的问题;
理解 "把自己锁死" :一个线程没有释放锁, 然后又尝试再次加锁。// 第一次加锁, 加锁成功lock();// 第二次加锁, 锁已经被占用, 阻塞等待.lock();按照之前对于锁的设定, 第二次加锁的时候, 就会阻塞等待. 直到第⼀次的锁被释放, 才能获取到第二 个锁. 但是释放第⼀个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无法进行 解锁操作. 这时候就会死锁。
这样的锁称为 不可重入锁。
Java 中的 synchronized 是 可重入锁, 因此没有上面的问题。
for (int i = 0; i < 50000; i++) {synchronized (locker) {synchronized (locker) {count++;}}
}
- 如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取到锁, 并让计数器自增.
- 解锁的时候计数器递减为 0 的时候, 才真正释放锁. (才能被别的线程获取到)
5.2 synchronized 使用示例
1) 修饰代码块: 明确指定锁哪个对象.
锁任意对象:
public class SynchronizedDemo {private Object locker = new Object();public void method() {synchronized (locker) {}}
}
锁当前对象:
public class SynchronizedDemo {public void method() {synchronized (this) {}}
}
2) 直接修饰普通方法: 锁的 SynchronizedDemo 对象
public class SynchronizedDemo {public synchronized void methond() {}
}
3) 修饰静态方法: 锁的 SynchronizedDemo 类的对象
public class SynchronizedDemo {public synchronized static void method() {}
}
5.3 Java 标准库中的线程安全类
- ArrayList
LinkedList HashMap TreeMap HashSet TreeSet StringBuilder
- Vector (不推荐使⽤)
- HashTable (不推荐使⽤)
- ConcurrentHashMap
- StringBuffer
StringBuffer 的核心方法都带有 synchronized .
相关文章:
【JavaEE初阶】线程安全问题及解决方法
目录 一、多线程带来的风险-线程安全 1、观察线程不安全 2、线程安全的概念 3、线程不安全的原因 4、解决之前的线程不安全问题 5、synchronized 关键字 - 监视器锁 monitor lock 5.1 synchronized 的特性 5.2 synchronized 使用示例 5.3 Java 标准库中的线程安全类…...
uniapp高德、百度、腾讯地图配置 SHA1
uniapp高德、百度、腾讯地图配置 SHA1 当winr弹出cmd弹框后输入 keytool -list -v -keystore debug.keystore 显示keytool 不是内部或外部命令,也不是可运行的程序或批处理文件。可以先看看是否有下载jdk且配置了环境变量,具体操作如下:keyto…...
[AutoSAR存储] 车载存储层次 和 常用存储芯片概述
公知及经验整理,原创保护,禁止转载。 专栏 《AutoSAR存储》 <<<< 返回总目录 <<<< 1 存储系统层次 先抛个问题, 为什么要划分存储器的层次? 速度越快,但成本越高,从经济的角度规…...
进程并发-信号量经典例题-吸烟者问题
1 题目描述 吸烟者问题 在一个房间内有三个吸烟者和一个香烟供应者。为了制造并抽掉香烟,每个吸烟者需要三样物品:烟草、纸和火柴,供应者有丰富物品提供。在三个吸烟者中,第一个有自己的烟草,第二个有自己的纸&#…...
[ruby on rails] array、jsonb字段
一、jsonb # 新增 add_column :shi_tis, :setting, :jsonb, default: {}# string转jsonb def changechange_column :users, :setting, :jsonb, using: setting::jsonb, default: {} end# 加索引 add_index :users, :setting, using: :gin # 这样就为setting jsonb字段创建了一…...
Feign接口请求返回异常 no suitable HttpMessageConvert found for response type
问题场景: 后端调用feign接口请求, 接口返回异常, no suitable HttpMessageConvert found for response type 问题描述 报错异常如下: //根据图片特征 去查询人员信息ResultVo<List> personVos ipbdFaceLibPersonApi.queryFacePersonByFeatur…...
【brpc学习实践九】mbvar及bvar可观测
概念、学习地址 mbvar中有两个类,分别是MVariable和MultiDimension,MVariable是多维度统计的基类,MultiDimension是派生模板类。也是主要用来多多线程计数用的。这里用到再详细去了解即可 https://github.com/luozesong/brpc/blob/master/do…...
Vue 3
Vu3 简述: 快速上手,开发即用,具体知识参考官方文档 具备知识 Vite 了解即可,使用时按需配置,更多参考官方文档( https://cn.vitejs.dev) 简述: 极速响应工具 (构建、启动、更新、插件使用等) 核心思想: 依赖 和 源码 工作原理: 引入rollup: 灵活,相比webpack速度快,…...
GitHub Copilot 替代品?
应该没人不知道代码补全这个东西了吧,第一次使用 GitHub Copilot 之后,只觉得真香,现在居然还有一点离不了了。后面因为收费原因,就没再用了,找了一个 tabnine 替代,用了几天,体验是真的比不上 …...
设计循环队列(详解)
呀哈喽,我是结衣 今天给大家带来的内容如标题所述,我们来设计环形队列,虽然队列没有讲,但是我就是想讲啊。那么环形队列现在开始。 队列的属性 在设计环形队列前,我们先要了解队列的特点(先进先出&#x…...
【Python】Vscode解决Python中制表符和空格混用导致的缩进问题
【Python】Vscode解决Python中制表符和空格混用导致的缩进问题 文章目录 【Python】Vscode解决Python中制表符和空格混用导致的缩进问题1. 问题来源2. 解决Reference 1. 问题来源 在python中使用缩进来进行代码块的分区,通常来说python的一个缩进包含4个空格&#…...
CocosCreator 面试题(十六)Cocos Creator 节点池的基本原理是什么?如何使用?
一、Cocos Creator 节点池的基本原理是什么? Cocos Creator 是一个游戏开发引擎,它提供了节点池(Node Pool)的功能,用于管理和重用游戏中的节点对象。节点池的基本原理如下: 创建初始节点:在游戏…...
VUE留言板
效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>作业</title><styl…...
【办公软件】电脑开机密码忘记了如何重置?
这个案例是家人的电脑,已经使用多年,又是有小孩操作过的,所以电脑密码根本不记得是什么了?那难道这台电脑就废了吗?需要重新装机吗?那里面的资料不是没有了? 为了解决以上问题,一般…...
PTA NeuDS-数据库题目集
一.判断题 1.在数据库中产生数据不一致的根本原因是冗余。T 解析:数据冗余是数据库中产生数据不一致的根本原因,因为当同一数据存储在多个位置时,如果其中一个位置的数据被修改,其他位置的数据就不一致了。因此,在数据…...
Redis深入理解-内核请求处理流程、数据传输协议
Redis 内核级请求处理流程 Redis Server 其实就是 Linux 服务器中的一个进程 主要还是下图的流程 应用先和 server 端建立 TCP 连接建立连接之后,server 端就会有一个与该客户端通信的 socket,客户端的读写请求发送到服务端的 socket那么通过 IO 多路…...
Mac电脑卸载/删除nodejs
使用命令行卸载 Node.js 第一步:打开终端,输入以下命令显示 Node.js 的安装路径: which node执行该命令后,会显示安装路径: /usr/local/bin/node第二步:输入以下命令删除 Node.js 相关的文件:…...
C语言之内存函数
C语言之内存函数 文章目录 C语言之内存函数1. memcpy 使⽤和模拟实现1.1 memcpy 函数的使用1.3 memcpy的模拟实现 2. memmove 使⽤和模拟实现2.1 memmove 函数的使用2.2 memmove的模拟实现 3. memset 函数的使用4. memcmp 函数的使⽤ 1. memcpy 使⽤和模拟实现 函数声明如下&a…...
基本数据结构二叉树(1)
目录 1.树概念及结构 1.1树的概念 1.2 树的相关概念 1.3 树的表示 1.4 树在实际中的运用(表示文件系统的目录树结构) 2.二叉树概念及结构 2.1概念 2.2现实中的二叉树: 2.3 特殊的二叉树: 2.5 二叉树的存储结构 2. 链式存…...
【python】Python将100个PDF文件对应的json文件存储到MySql数据库(源码)【独一无二】
👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉公众号👈:测试开发自动化【获取源码商业合作】 👉荣__誉👈:阿里云博客专家博主、5…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...

