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

<JavaEE> 什么是线程安全?产生线程不安全的原因和处理方式

目录

一、线程安全的概念

二、线程不安全经典示例

三、线程不安全的原因和处理方式

3.1 线程的随机调度和抢占式执行

3.2 修改共享数据

3.3 关键代码或指令不是“原子”的

3.4 内存可见性和指令重排序

四、Java标准库自带的线程安全类


一、线程安全的概念

线程安全是指:某段代码无论是在单线程还是多线程的环境下执行,结果都是正确的或符合心理预期的。
通常情况下,如果一段代码在单线程环境下执行和在多线程环境下执行的结果不一致,那么就很可能存在线程安全的问题,这个情况就称为“线程不安全”。
线程安全问题是多线程编程的重点!

二、线程不安全经典示例

class AddTest{public static  int count = 0;public void add(){count++;}
}
public class Synchronized_Demo0 {public static void main(String[] args) throws InterruptedException {//创建一个AddTest实例;AddTest test1 = new AddTest();//两个循环调用add方法进行count++的线程;Thread t1 = new Thread(()->{for (int i = 0; i < 5000; i++) {test1.add();}});Thread t2 = new Thread(()->{for (int i = 0; i < 5000; i++) {test1.add();}});//启动线程;t1.start();t2.start();//阻塞main线程;t1.join();t2.join();//打印count;System.out.println("count = :"+AddTest.count);}
}//第一次运行结果:
count = 8061
//第二次运行结果:
count = 7269
//第三次运行结果:
count = 9792

在单线程环境下,两个5000次循环的count++,得到的结果应该是count = 10000。

上述代码在多线程的环境下,得到的结果却是count = 8061。

而且,每次运行得到的结果都会不同。
这就出现了线程安全问题。

三、线程不安全的原因和处理方式

线程不安全的五个原因
1)线程的随机调度和抢占式执行(根本原因)。
2)修改共享数据。
3)关键代码或指令不是“原子”的(直接原因)。
4)内存可见性。
5)指令重排序。

3.1 线程的随机调度和抢占式执行

原因和处理方式:

原因操作系统上的线程是“随机调度”和“抢占式执行”的,这是产生线程安全问题的根本原因。
处理方式这个原因是由操作系统本身决定的,无法改变。

3.2 修改共享数据

前置知识点:

上述代码中,“count++;”这一句代码实际上是由三条系统指令完成的。包括以下三条指令:
load(读取):从内存中读取数据;
add(运算):进行数据运算;
save(写入):将运算后的数据写入内存中;

根据下图进行分析:

在示例代码中,有多个线程在“同一时刻”,访问了同一变量(count),这个变量就是一个“共享数据”。

通过以上分析,我们发现可以使用两个不同的变量自增,自增完成后再相加,可以解决这里的线程不安全问题。

原因和处理方式:

原因多个线程修改同一个变量。
处理方式这是一个与代码结果相关的问题,有时可以通过调整代码结构解决,但有时代码结构是无法调整的。

3.3 关键代码或指令不是“原子”的

根据下图进行分析:

原因和处理方式:

原因代码中影响线程安全的关键代码或指令不是“原子”的。这是产生线程不安全的直接原因。
处理方式使用 synchronized 关键字加锁,是代码或指令实现逻辑上的原子性,即当线程在执行这些代码或指令时,要么全部不执行,要么全部执行完毕后其他线程才能进入。

应该注意,这里的加锁并不是让代码或指令实现真正的原子性。

也就是说不是真的在执行加锁的代码或指令时不让线程被调度走,而是线程仍可以“暂时离开”。

但此时其他线程也不得进入这段被执行了“一半”的加锁代码或指令。

如上文所讲的,是“打包”为一个逻辑上的整体。

阅读指针 -> 《 synchronized 关键字 和 锁机制 》

<JavaEE> synchronized关键字和锁机制 -- 锁的特点、锁的使用、锁竞争和死锁、死锁的解决方法-CSDN博客Java中加锁的方式有很多种,其中使用 synchronized 关键字进行加锁是最常用的。synchronized 是一种监视器锁(monitor lock)。是为了将多个操作“打包”为一个有“原子性”的操作。进行加锁的时候必须先准备好“锁对象”,锁对象可以是任何类型的实例。synchronized 的底层是使用操作系统的 mutex lock 实现的,本质上依然是调用系统的 API ,依靠 CPU 的特定指令完成加锁功能的。https://blog.csdn.net/zzy734437202/article/details/134742168

3.4 内存可见性和指令重排序

原因:内存可见性指的是一个线程对共享数据的修改,能否即使被其他线程观测到。如果没有,那么其他线程则无法获得正确数据。
原因:

指令重排序是指在“保证程序执行逻辑不变”的情况下,改变指令的执行顺序,以提高编译器的运行效率。

指令重排序在单线程下非常有效,但在多线程下对于“保证程序执行逻辑不变”这一条件判断难度高,因此会出现因为指令重排序而导致的线程不安全。

处理方式:

以上的两个产生线程不安全的原因都来自于编译器优化,本意是通过调整指令执行顺序,提高程序效率,但在多线程环境下,会导致线程不安全。

为应对这种情况,Java提供了 volatile 关键字,用于强制关闭编译器优化。

volatile 关键字的两大核心功能就是保证内存可见性和进制指令重排序。

阅读指针 -> 《 volatile 关键字的 功能 和 使用 》

<JavaEE> volatile关键字 -- 保证内存可见性、禁止指令重排序-CSDN博客简单介绍什么是内存可见性和指令重排序。volatile关键字可以将这两种编译器优化强制关闭。https://blog.csdn.net/zzy734437202/article/details/134757070


四、Java标准库自带的线程安全类

        Java标准库中,有很多类虽然涉及多线程修改共享数据,但又没有加锁措施,因此他们都是线程不安全的。

线程不安全的类:

ArrayList
LinkedList
HashMap
TreeMap
HashSet
TreeSet
StringBuiler

当然,也有一些线程安全的类。

以下的类通过锁机制使得在多线程下,产生线程安全问题的概率大大降低了。

线程安全的类:Vector
HashTable
ConcurrentHashMap
StringBuffer
可以看到有一些类被添加了删除线。是的,根据官方文档,推荐不再使用这几个类,因为它们即将被标准库弃用。
String字符串类比较特殊,因为String本身就是不可变的,因此它就是线程安全的。

阅读指针 -> 《 多线程编程中的“等待和通知机制”:wait 和 notify 方法 》

<JavaEE> 多线程编程中的“等待和通知机制”:wait 和 notify 方法-CSDN博客文章浏览阅读8次。介绍了由 wait 和 notify 方法组成的等待和通知机制。https://blog.csdn.net/zzy734437202/article/details/134774218

相关文章:

<JavaEE> 什么是线程安全?产生线程不安全的原因和处理方式

目录 一、线程安全的概念 二、线程不安全经典示例 三、线程不安全的原因和处理方式 3.1 线程的随机调度和抢占式执行 3.2 修改共享数据 3.3 关键代码或指令不是“原子”的 3.4 内存可见性和指令重排序 四、Java标准库自带的线程安全类 一、线程安全的概念 线程安全是指…...

Kotlin 中的 also 和 run:选择正确的作用域函数

在 Kotlin 中&#xff0c;also 和 run 是两个十分有用的作用域函数。 虽然它们在功能上相似&#xff0c;但各自有独特的用途和适用场景。 一、分析&#xff1a; also&#xff1a;在对象的上下文中执行给定的代码块&#xff0c;并返回对象本身。它的参数是一个接收对象并返回…...

ZKP Understanding Nova (1): MinRoot Example

Understanding Nova Kothapalli, Abhiram, Srinath Setty, and Ioanna Tzialla. “Nova: Recursive zero-knowledge arguments from folding schemes.” Annual International Cryptology Conference. Cham: Springer Nature Switzerland, 2022. Nova: Paper Code 1. Unders…...

0基础学java-day14

一、集合 前面我们保存多个数据使用的是数组&#xff0c;那么数组有不足的地方&#xff0c;我们分析一下 1.数组 2 集合 数据类型也可以不一样 3.集合的框架体系 Java 的集合类很多&#xff0c;主要分为两大类&#xff0c;如图 &#xff1a;[背下来] package com.hspedu.c…...

创建conan包-工具链

创建conan包-工具链 1 Toolchains 本文是基于对conan官方文档Toolchains翻译而来&#xff0c; 更详细的信息可以去查阅conan官方文档。 1 Toolchains Toolchains are the new way to integrate with build systems in Conan. Recipes can define a generate() method that wi…...

IntelliJ IDE 插件开发 | (二)UI 界面与数据持久化

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门 前言 在上一篇文章中介绍了在IDEA下开发、运行和安装插件的基本步骤&#xff0c;因此创建项目等基础步骤不再赘述&#xff0c;本文则开始介绍如何进行 UI 界面的开发以及相关数据的持久化存储&#xff…...

使用vue UI安装路由插件

1.使用vue创建项目 vue create vue-appvue ui 2.使用vue ui界面创建管理项目 终端页面输入&#xff1a;vue ui 创建项目 安装完成。可以直接在ui界面运行&#xff0c;也可以在编辑器中使用命令运行 安装路由&#xff0c;安装状态 选择插件 - 添加vue-router、添加vuex 安装…...

RPG项目01_脚本代码

基于“RPG项目01_场景及人物动画管理器”&#xff0c;我们创建一个XML文档 在资源文件夹下创建一个文件夹&#xff0c; 命名为Xml 将Xnl文档拖拽至文件夹中&#xff0c; 再在文件夹的Manager下新建脚本LoadManager 写代码&#xff1a; using System.Collections; using System…...

目标检测YOLO实战应用案例100讲-交通目标数据集构建及高性能检测算法研究与应用

目录 前言 国内外研究现状 目标检测研究现状 目标检测数据集研究现状...

浅谈Vue.js的计算属性computed

什么是computed属性 computed 属性用于声明计算属性&#xff0c;这些属性的值是基于其他响应式属性计算而来的&#xff0c;当依赖的响应式属性发生变化时&#xff0c;计算属性会自动重新计算。 与Vue.js 2相比&#xff0c;Vue.js 3的 computed 属性语法稍有变化&#xff0c;不…...

Linux常用指令详解

目录 前言&#xff1a; Linux的目录结构 Linux常用指令简介 whoami指令 ls指令 pwd指令 cd指令 tree指令 touch指令 mkdir指令 rmdir指令与rm指令 man指令 cp&#xff08;copy&#xff09;指令 mv&#xff08;move&#xff09;指令 cat指令 重定向及重定向的类型…...

Nginx(性能优化)

到这里文章的篇幅较长了&#xff0c;最后再来聊一下关于Nginx的性能优化&#xff0c;主要就简单说说收益最高的几个优化项&#xff0c;在这块就不再展开叙述了&#xff0c;毕竟影响性能都有多方面原因导致的&#xff0c;比如网络、服务器硬件、操作系统、后端服务、程序自身、数…...

机器学习笔记 - 如何在Python中对网格和点云进行体素化?

一、简述 本文主要是为了了解如何生成体素表示,体素之于3D就像像素之于2D。体素本质上是 3D 像素,但它们不是正方形,而是完美的立方体。 理论上,体素是复制现实的完美建模技术。 这里我们要了解四个广泛流行的 Python 库(Open3D、Trimesh、PyVista、pyntcloud )生成点云…...

冒个泡!OceanBase亮相 2023 新加坡金融科技节

近日&#xff0c;OceanBase 亮相 Singapore Fintech Festival 2023&#xff08;2023 新加坡金融科技节&#xff09;&#xff01;本届新加坡金融科技节于 2023 年 11 月 15 日至 17 日在新加坡博览展览中心举行&#xff0c;展会期间&#xff0c;OceanBase 得到了众多金融科技机构…...

正则表达式(5):常用符号

正则表达式&#xff08;5&#xff09;&#xff1a;常用符号 小结 本博文转载自 在本博客中&#xff0c;”正则表达式”为一系列文章&#xff0c;如果你想要从头学习怎样在Linux中使用正则&#xff0c;可以参考此系列文章&#xff0c;直达链接如下&#xff1a; 在Linux中使用正…...

Web安全漏洞分析-XSS(下)

随着互联网的迅猛发展&#xff0c;Web应用的普及程度也愈发广泛。然而&#xff0c;随之而来的是各种安全威胁的不断涌现&#xff0c;其中最为常见而危险的之一就是跨站脚本攻击&#xff08;Cross-Site Scripting&#xff0c;简称XSS&#xff09;。XSS攻击一直以来都是Web安全领…...

金南瓜SECS/GEM C# SDK 快速使用指南

本文对如何使用金南瓜SECS/GEM C# SDK 快速创建一个满足SECS/GEM通信要求的应用程序&#xff0c;只需简单3步完成。 第一步&#xff1a;创建C# .NET程序 示例使用Visual Studio 2010&#xff0c;使用者可以选择更高级版本 Visual Studio 第二步&#xff1a;添加DLL库引用&am…...

在一个没有超级用户的mongodb 生产库上如何添加超级用户

说来这个问题&#xff0c;都觉得不可思议&#xff0c;一个数据库怎么没有超级用户呢&#xff0c;我们知道&#xff0c;MYSQL&#xff0c;PG&#xff0c;ORACLE等&#xff0c;创建好后&#xff0c;都有一个默认的超级用户&#xff0c;MONGODB也有超级用户&#xff0c;但需要自己…...

排序算法之二:冒泡排序

冒泡排序的思路 冒泡排序是交换排序 基本思想&#xff1a;所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键值较大的记录向序列的尾部移动&#xff0c;键值较小的记录向序列的前部移动…...

一键搭建你的hnust请假条

hnust请假条 湖南科技大学请假条生成器 https://hnust.rick.icu/new &#xff08;直接使用&#xff09; Hnust Leave Note 去github https://github.com/rickhqh/hnust_leave_note 效果展示 界面展示效果图 v2.0 更新 vant和vue重构了整个源码同步学校新版请假条样式修复了…...

C练习题13

单项选择题(本大题共20小题,每小题2分,共40分。在每小题给出的四个备选项中,选出一个正确的答案,并将所选项前的字母填写在答题纸的相应位置上。) 1.结构化程序由三种基本结构组成、三种基本结构组成的算法是() A.可以完成任何复杂的任务 B. 只能完成部分复杂的任务 C. 只能完…...

交易历史记录20231206 记录

昨日回顾&#xff1a; select top 10000 * from dbo.CODEINFO A left join dbo.全部&#xff21;股20231206010101 B ON A.CODE B.代码 left join dbo.全部&#xff21;股20231206CONF D on A.CODED.代码left join dbo.全部&#xff21;股20231206 G on A.CODEG.代码 left…...

1-5总体分布的推断

...

深信服技术认证“SCSA-S”划重点:XSS漏洞

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…...

MIT6S081-Lab2总结

大家好&#xff0c;我叫徐锦桐&#xff0c;个人博客地址为www.xujintong.com&#xff0c;github地址为https://github.com/xjintong。平时记录一下学习计算机过程中获取的知识&#xff0c;还有日常折腾的经验&#xff0c;欢迎大家访问。 Lab2就是了解一下xv6的系统调用流程&…...

CMMI5大成熟度等级和4大过程域

CMMI&#xff08;Capability Maturity Model Integration&#xff0c;能力成熟度模型集成&#xff09;模型系列是帮助组织改进其过程的最佳实践的集合。这些模型由来自产业界、政府以及软件工程研究所&#xff08;Software Engineering Institute&#xff0c; SEI&#xff09;的…...

c++新经典模板与泛型编程:const修饰符的移除与增加

const修饰符的移除 让你来写移除const修饰符&#xff0c;你会怎么样来写&#xff1f; &#x1f602;&#x1f602;trait类模板&#xff0c;如下 #include <iostream>// 泛化版本 template<typename T> struct RemoveConst {using type T; };// 特化版本 template…...

AUTOSAR汽车电子嵌入式编程精讲300篇-基于加密算法的车载CAN总线安全通信

目录 前言 研究现状 系统架构研究 异常检测研究 认证与加密研究 相关技术 2.1车联网 2.2车载网络及总线 2.2.1 CAN总线基础 2.2.2 CAN总线网络安全漏洞 2.2.3 CAN总线信息安全需求 2.3密码算法 2.3.1 AES算法 2.3.2 XTEA算法 CAN网络建模与仿真 3.1 CAN网络建模…...

4-Docker命令之docker start

1.docker start介绍 docker start命令是用来启动一个或多个已经被停止的docker容器。 2.docker start用法 docker start [参数] container [container......] [root@centos79 ~]# docker start --helpUsage: docker start [OPTIONS] CONTAINER [CONTAINER...]Start one or…...

AWS Remote Control ( Wi-Fi ) on i.MX RT1060 EVK - 2 “架构 AWS”

接续上一章节&#xff0c;我们把开发环境架设好之后&#xff0c;此章节叙述如何建立 AWS IoT 环境&#xff0c;请务必已经有 AWS Account&#xff0c;申请 AWS Account 之流程将不在此说明。 III-1. 登入AWS IoT&#xff0c; 在“管理”>“所有装置”>“实物”下点击“建…...