Java: 线程安全问题的解决方案(synchronized)

发生原因

要想解决线程安全问题,那么我们首先得知道线程安全问题为什么会发生.

发生原因:

  1. 线程在操作系统中是"随机调度,抢占式执行的"[根本原因].
  2. 多个线程,同时修改同一个变量
  3. 修改操作不是"原子"的
  4. 内存可见性问题
  5. 指令重排序

解决方案

原因1和2,我们很难去改变.本文主要介绍3 . 4 , 5 后面再说.

解决线程安全问题,最主要的办法就是把"非原子"的修改,变成"原子".也就是 ---- 加锁.
此处的加锁并不是真的让 count++ 变成原子的,也没有干预到线程的调度,只不过是通过这种加锁的方式,使一个线程在执行count++的过程中,其他的线程的count++不能插队进来~

synchronized关键字

Java中提供了synchronized关键字来完成加锁操作.

synchronized ()

synchronized ( ) 是关键字,不是函数,后面的()并非是参数,而是"锁对象". ( )里的对象可以指定任何的对象.
在这里插入图片描述

public class Demo9 {//锁对象private static Object locker = new Object();private static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {//加锁synchronized (locker) {count++;}}});Thread t2 = new Thread(()->{for(int i = 0; i < 50000;i++) {//加锁synchronized (locker) {count++;}}});//创建线程t1.start();t2.start();//让main线程等待t1和t2t1.join();t2.join();System.out.println(count);}
}

在这里插入图片描述
上述代码有效的前提是,两个线程都加锁了,而且是针对同一个对象加锁了!!!

锁对象的作用就是来区分两个线程是否是针对"同一个对象"加锁.

  • 是针对同一个对象加锁,此时就会出现"阻塞"(锁竞争 / 锁冲突)
  • 不是针对同一个对象加锁,此时不会出现"阻塞",两个线程仍然是随机调度的并发执行.
  • 锁对象,填哪个对象不重要,重要的是,多个线程是否是同一个对象~

刚才看到的情况是两个线程,针对一个锁加锁的,如果是多线程呢?

如果是3个线程针对同一个对象加锁~

也是类似的情况
其中某个线程先加上锁,另外两个线程阻塞等待,(哪个线程拿到锁,这个过程是不可预期的)

拿到锁的线程释放了锁之后,剩下的两个线程谁先拿到锁呢? 也是顺序不确定的!

1 2 3
比如最开始 1 拿到锁, 2 3 阻塞等待
1释放锁之后,2 和 3 谁先拿到锁? 不一定! 也是随机的,即使 2 先加锁,3后加锁,也不一定谁先拿到~


此处的 synchronized 是JVM 提供的功能,synchronized 底层实现就是JVM中通过C++代码来实现的
进一步的,也是依靠操作系统的 api 实现的加锁, 操作系统的 api 则是来自于cpu上支持的特殊的指令来实现的~
在这里插入图片描述

c++ / Python 加锁是一个函数,解锁是一个函数
像Java这种通过 synchronized 关键字,来同时完成加锁解锁,是比较少见的


synchronized 还可以修饰一个方法

synchronized 修饰普通的方法:

public void add() {synchronized (this) {count++;}
}

等价于

// 针对这个写法,锁对象就是this
synchronized public void add() {count++;
}

synchronized 修饰static方法:

class Counter {public static void func() {synchronized (Counter.class) {}}
}

等价于

class Counter {synchronized public static void func() {}
}

并非是写了synchronized 就一定线程安全,还是要看代码具体咋写~

换而言之,到底是否要加synchronized,怎么加锁,都是和具体的场景直接相关的

像上面那种"无脑加锁"的做法是不推荐的
使用锁是需要付出代价的(性能),使用锁就可能触发阻塞,一旦某个线程阻塞,啥时候能恢复阻塞,继续执行,是不可预期的(可能要非常多时间)

总结一下

synchronized 几种使用方式:

  1. synchronized ( ) { }
    圆括号指定锁对象
  2. synchronized 修饰一个普通的方法
    相当于针对 this 加锁
  3. synchronized 修饰一个静态的方法
    相当于针对 对应的类对象 加锁

锁对象:

  1. 可以吧任意的 Object / Object 子类的对象,作为锁对象
  2. 锁对象是啥不重要,重要的是,两个线程的锁对象是否是同一个
    • 是同一个,才会出现 阻塞 / 锁竞争
    • 不是同一个,不会出现 阻塞 / 锁竞争

本文到这里就结束啦~

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

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

相关文章

04:【stm32】LED编程和按键控制

LED编程和按键控制 1、LED编程1.1、点亮一课LED灯 2、按键控制2.1、通过一个按钮控制LED灯的亮灭 1、LED编程 1.1、点亮一课LED灯 首先&#xff0c;我们想象一下&#xff0c;让LED灯点亮&#xff0c;引脚应该是输出模式&#xff0c;那么应该是通用模式&#xff0c;还是复用模式…

打靶记录7——Hacker_Kid-v1.0.1

靶机下载地址 https://download.vulnhub.com/hackerkid/Hacker_Kid-v1.0.1.ova难度 OSCP 风格的中级难度靶机&#xff08;只需要获取root权限即可&#xff0c;CTF 风格的靶机就还需要获取flag&#xff09; 涉及的攻击方法&#xff1a; 主机发现端口扫描Web信息收集DNS区域传…

Redis2-Redis常见命令

目录 Redis数据结构介绍 Redis通用命令 KEYS DEL EXISTS EXPIRE String类型 Key的层级格式 Hash类型 List类型 Set类型 SortedSet类型 Redis数据结构介绍 Redis是一个key-value的数据库&#xff0c;key一般是String数据库&#xff0c;value的类型多种多样 可以通过…

《Unity3D网络游戏实战》学习与实践--制作一款大乱斗游戏

角色类 基类Base Human是基础的角色类&#xff0c;它处理“操控角色”和“同步角色”的一些共有功能&#xff1b;CtrlHuman类代表“操控角色”​&#xff0c;它在BaseHuman类的基础上处理鼠标操控功能&#xff1b;SyncHuman类是“同步角色”类&#xff0c;它也继承自BaseHuman&…

解决电脑缺少.NET组件?手把手教你轻松解决

在日常使用电脑的过程中&#xff0c;很多用户可能会遇到“电脑缺少.NET组件”的提示&#xff0c;这可能导致某些应用程序无法正常运行或安装。那么&#xff0c;.NET组件到底是什么&#xff1f;为何它如此重要&#xff1f;本文将为您详细解答这些问题&#xff0c;并提供有效的解…

[ACM MM 2024] Wave-Mamba:超高清暗光图像增强的小波状态空间模型

Wave-Mamba: Wavelet State Space Model for Ultra-High-Definition Low-Light Image Enhancement (arxiv.org) Wave-Mamba是一种用于增强超高清低光照图像的新模型&#xff0c;它引入了低频状态空间块和高频增强块&#xff0c;并取得了领先水平的性能。该模型即将开源&#x…

用Python插入表格到PowerPoint演示文稿

有效的信息传达是演示文稿中的重点&#xff0c;而PowerPoint演示文稿作为最广泛使用的演示工具之一&#xff0c;提供了丰富的功能来帮助演讲者实现这一目标。其中&#xff0c;在演示文稿中插入表格可以帮助观众更直观地理解数据和比较信息。通过使用Python这样的强大编程语言&a…

【STL】 vector的底层实现

1.vector的模拟代码完整实现&#xff08;后面会拆分开一个一个细讲&#xff09; #pragma once #include<assert.h>// 抓重点namespace bit {/*template<class T>class vector{public:typedef T* iterator;private:T* _a;size_t _size;size_t _capacity;};*/templa…

Python(模块)

模块编写完成就可以被其他模块进行调用并使用被调用模块的功能。 import导入方式的语法结构&#xff1a; import模块名称【as别名】 from……import导入方式的语法结构&#xff1a; from模块名称&#xff0c;import变量/函数/类/*&#xff08;*是通配符&#xff09; impor…

Milvus 向量数据库进阶系列丨构建 RAG 多租户/多用户系统 (上)

本系列文章介绍 在和社区小伙伴们交流的过程中&#xff0c;我们发现大家最关心的问题从来不是某个具体的功能如何使用&#xff0c;而是面对一个具体的实战场景时&#xff0c;如何选择合适的向量数据库解决方案或最优的功能组合。在 “Milvus 向量数据库进阶” 这个系列文章中&…

【生成式AI-二-强大的AI下我们可以做什么】

强大的AI下我们可以做什么 人工智能的厉害之处我们可以作什么评估模型好坏的难度prompt engineering微调fine tune 人工智能的厉害之处 人工智能并不是忽然就爆火的&#xff0c;事实上&#xff0c;很久以前就已经有深度学习、机器学习这些概念了&#xff0c;那现在的人工智能和…

Java之类和对象

目录 static关键字 1修饰属性 2修饰方法 final 构造方法 基本语法 this关键字 代码块 定义 普通代码块 构造代码块 静态代码块 匿名对象 toString 总结 static关键字 1修饰属性 Java的静态属性和类相关, 和具体的实例无关. 换句话说, 同一个类的不同实例共用同一个…

反转链表 II(LeetCode)

题目 给你单链表的头指针 和两个整数 和 &#xff0c;其中 。请你反转从位置 到位置 的链表节点&#xff0c;返回 反转后的链表 。 解题 class ListNode:def __init__(self, value0, nextNone):self.value valueself.next nextdef reverseBetween(head: ListNode, lef…

crm客户管理系统有哪些?盘点大家使用最广泛的15款

将对比的客户管理CRM系统包括&#xff1a;纷享销客、Zoho CRM、销售易、用友CRM、Salesforce、Microsoft Dynamics 365、销帮帮CRM、HubSpot、Oracle CRM、悟空CRM、神州云动CRM、红圈CRM、SAP CRM、Odoo、OroCRM。 一个合适的CRM系统可以极大地提高你的销售效率和客户满意度&a…

SpringMVC执行流程

1 流程对比 1.1 原生servlet开发流程 根据需求编写servlet程序在web.xml 中通过配置&#xff0c;指定servlet所能处理的请求&#xff0c;即建立servlet与请求路径间的映射在Servlet的service方法中对路径进行再判断&#xff0c;从而进行具体的逻辑处理servlet参数从request中…

数据结构-递归

用递归代替循环 假设工作中的你&#xff0c;需要写一个倒数程序。该程序接收一个数字&#xff0c;例如10&#xff0c;然后显示从10到0的数字。现在先暂停一下&#xff0c;选择一门编程语言来实现这个程序&#xff0c;做完以后&#xff0c;再往下阅读。或许你用了JavaScript&am…

数学建模--二分法

目录 二分法的基本原理 应用实例 求解方程根 查找有序数组中的元素 注意事项 Python代码示例 ​编辑 延伸 二分法在数学建模中的具体应用案例有哪些&#xff1f; 如何选择二分法的初始区间以确保收敛速度和精度&#xff1f; 在使用二分法求解方程时&#xff0c;如何…

排序算法2:直接选择排序与快速排序

目录 1.直接选择排序 1.1直接选择排序的优化 2.快速排序 2.1基准值的置位&#xff08;Hoare版&#xff09; 2.2挖坑法 2.3lomuto前后指针 前言 前面我们进入了排序算的讲解。今天我们将继续学习几种重要的排序思想&#xff0c;好&#xff0c;咱们三连上车开始今天的内容。…

ChatTTS文本转语音本地部署结合内网穿透实现远程使用生成AI音频

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 本篇文章主要介绍如何快速地在Windows系统电脑中本地部署ChatTTS开源文本转语音项目&#xff0c;并且我们还可以结合Cpolar内网穿透工具创建公网地址&#xff0c;随时随…

动态规划.

目录 &#xff08;一&#xff09;递归到动规的一般转化方法 &#xff08;二&#xff09;动规解题的一般思路 1. 将原问题分解为子问题 2. 确定状态 3. 确定一些初始状态&#xff08;边界状态&#xff09;的值 4. 确定状态转移方程 &#xff08;三&#xff09;能用动规解…