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

Java之原子性问题的解决

2. 原子性

2.1 volatile-问题

代码分析 :

package com.itheima.myvolatile;
​
public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("小路同学");t1.start();
​MyThread2 t2 = new MyThread2();t2.setName("小皮同学");t2.start();}
}
package com.itheima.myvolatile;
​
public class Money {public static int money = 100000;
}
package com.itheima.myvolatile;
​
public class MyThread1 extends  Thread {@Overridepublic void run() {while(Money.money == 100000){
​}
​System.out.println("结婚基金已经不是十万了");}
}
​
package com.itheima.myvolatile;
​
public class MyThread2 extends Thread {@Overridepublic void run() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
​Money.money = 90000;}
}

程序问题 : 女孩虽然知道结婚基金是十万,但是当基金的余额发生变化的时候,女孩无法知道最新的余额。

2.2 volatile解决

以上案例出现的问题 :

当A线程修改了共享数据时,B线程没有及时获取到最新的值,如果还在使用原先的值,就会出现问题

1,堆内存是唯一的,每一个线程都有自己的线程栈。

2 ,每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中。

3 ,在线程中,每一次使用是从变量的副本中获取的。

Volatile关键字 : 强制线程每次在使用的时候,都会看一下共享区域最新的值

代码实现 : 使用volatile关键字解决

package com.itheima.myvolatile;
​
public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("小路同学");t1.start();
​MyThread2 t2 = new MyThread2();t2.setName("小皮同学");t2.start();}
}
package com.itheima.myvolatile;
​
public class Money {public static volatile int money = 100000;
}
package com.itheima.myvolatile;
​
public class MyThread1 extends  Thread {@Overridepublic void run() {while(Money.money == 100000){
​}
​System.out.println("结婚基金已经不是十万了");}
}
​
package com.itheima.myvolatile;
​
public class MyThread2 extends Thread {@Overridepublic void run() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
​Money.money = 90000;}
}
​

2.3 synchronized解决

synchronized解决 :

1 ,线程获得锁

2 ,清空变量副本

3 ,拷贝共享变量最新的值到变量副本中

4 ,执行代码

5 ,将修改后变量副本中的值赋值给共享数据

6 ,释放锁

代码实现 :

package com.itheima.myvolatile2;
​
public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.setName("小路同学");t1.start();
​MyThread2 t2 = new MyThread2();t2.setName("小皮同学");t2.start();}
}
package com.itheima.myvolatile2;
​
public class Money {public static Object lock = new Object();public static volatile int money = 100000;
}
package com.itheima.myvolatile2;
​
public class MyThread1 extends  Thread {@Overridepublic void run() {while(true){synchronized (Money.lock){if(Money.money != 100000){System.out.println("结婚基金已经不是十万了");break;}}}}
}
package com.itheima.myvolatile2;
​
public class MyThread2 extends Thread {@Overridepublic void run() {synchronized (Money.lock) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
​Money.money = 90000;}}
}

2.4 原子性

概述 : 所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体。

代码实现 :

package com.itheima.threadatom;
​
public class AtomDemo {public static void main(String[] args) {MyAtomThread atom = new MyAtomThread();
​for (int i = 0; i < 100; i++) {new Thread(atom).start();}}
}
class MyAtomThread implements Runnable {private volatile int count = 0; //送冰淇淋的数量
​@Overridepublic void run() {for (int i = 0; i < 100; i++) {//1,从共享数据中读取数据到本线程栈中.//2,修改本线程栈中变量副本的值//3,会把本线程栈中变量副本的值赋值给共享数据.count++;System.out.println("已经送了" + count + "个冰淇淋");}}
}

代码总结 : count++ 不是一个原子性操作, 他在执行的过程中,有可能被其他线程打断

2.5 volatile关键字不能保证原子性

解决方案 : 我们可以给count++操作添加锁,那么count++操作就是临界区中的代码,临界区中的代码一次只能被一个线程去执行,所以count++就变成了原子操作。

package com.itheima.threadatom2;
​
public class AtomDemo {public static void main(String[] args) {MyAtomThread atom = new MyAtomThread();
​for (int i = 0; i < 100; i++) {new Thread(atom).start();}}
}
class MyAtomThread implements Runnable {private volatile int count = 0; //送冰淇淋的数量private Object lock = new Object();
​@Overridepublic void run() {for (int i = 0; i < 100; i++) {//1,从共享数据中读取数据到本线程栈中.//2,修改本线程栈中变量副本的值//3,会把本线程栈中变量副本的值赋值给共享数据.synchronized (lock) {count++;System.out.println("已经送了" + count + "个冰淇淋");}}}
}

2.6 原子性_AtomicInteger

概述:java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。因为变

量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。本次我们只讲解

使用原子的方式更新基本类型,使用原子的方式更新基本类型Atomic包提供了以下3个类:

AtomicBoolean: 原子更新布尔类型

AtomicInteger: 原子更新整型

AtomicLong: 原子更新长整型

以上3个类提供的方法几乎一模一样,所以本节仅以AtomicInteger为例进行讲解,AtomicInteger的常用方法如下:

public AtomicInteger():                 初始化一个默认值为0的原子型Integer
public AtomicInteger(int initialValue):  初始化一个指定值的原子型Integer
​
int get():                              获取值
int getAndIncrement():                   以原子方式将当前值加1,注意,这里返回的是自增前的值。
int incrementAndGet():                   以原子方式将当前值加1,注意,这里返回的是自增后的值。
int addAndGet(int data):                 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
int getAndSet(int value):                以原子方式设置为newValue的值,并返回旧值。

代码实现 :

package com.itheima.threadatom3;
​
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomIntergerDemo1 {
//    public AtomicInteger():                  初始化一个默认值为0的原子型Integer
//    public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integerpublic static void main(String[] args) {AtomicInteger ac = new AtomicInteger();System.out.println(ac);
​AtomicInteger ac2 = new AtomicInteger(10);System.out.println(ac2);}
​
}
package com.itheima.threadatom3;
​
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomIntergerDemo2 {
//    int get():                获取值
//    int getAndIncrement():     以原子方式将当前值加1,注意,这里返回的是自增前的值。
//    int incrementAndGet():     以原子方式将当前值加1,注意,这里返回的是自增后的值。
//    int addAndGet(int data):   以原子方式将参数与对象中的值相加,并返回结果。
//    int getAndSet(int value):  以原子方式设置为newValue的值,并返回旧值。public static void main(String[] args) {
//        AtomicInteger ac1 = new AtomicInteger(10);
//        System.out.println(ac1.get());
​
//        AtomicInteger ac2 = new AtomicInteger(10);
//        int andIncrement = ac2.getAndIncrement();
//        System.out.println(andIncrement);
//        System.out.println(ac2.get());
​
//        AtomicInteger ac3 = new AtomicInteger(10);
//        int i = ac3.incrementAndGet();
//        System.out.println(i);//自增后的值
//        System.out.println(ac3.get());
​
//        AtomicInteger ac4 = new AtomicInteger(10);
//        int i = ac4.addAndGet(20);
//        System.out.println(i);
//        System.out.println(ac4.get());
​AtomicInteger ac5 = new AtomicInteger(100);int andSet = ac5.getAndSet(20);System.out.println(andSet);System.out.println(ac5.get());}
}

2.7 AtomicInteger-内存解析

AtomicInteger原理 : 自旋锁 + CAS 算法

CAS算法:

有3个操作数(内存值V, 旧的预期值A,要修改的值B)

当旧的预期值A == 内存值 此时修改成功,将V改为B

当旧的预期值A!=内存值 此时修改失败,不做任何操作

并重新获取现在的最新值(这个重新获取的动作就是自旋)

2.8 AtomicInteger-源码解析

代码实现 :

package com.itheima.threadatom4;
​
public class AtomDemo {public static void main(String[] args) {MyAtomThread atom = new MyAtomThread();
​for (int i = 0; i < 100; i++) {new Thread(atom).start();}}
}
package com.itheima.threadatom4;
​
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomThread implements Runnable {//private volatile int count = 0; //送冰淇淋的数量//private Object lock = new Object();AtomicInteger ac = new AtomicInteger(0);
​@Overridepublic void run() {for (int i = 0; i < 100; i++) {//1,从共享数据中读取数据到本线程栈中.//2,修改本线程栈中变量副本的值//3,会把本线程栈中变量副本的值赋值给共享数据.//synchronized (lock) {
//                count++;
//                ac++;int count = ac.incrementAndGet();System.out.println("已经送了" + count + "个冰淇淋");// }}}
}
​

源码解析 :

//先自增,然后获取自增后的结果
public final int incrementAndGet() {//+ 1 自增后的结果//this 就表示当前的atomicInteger(值)//1    自增一次return U.getAndAddInt(this, VALUE, 1) + 1;
}
​
public final int getAndAddInt(Object o, long offset, int delta) {//v 旧值int v;//自旋的过程do {//不断的获取旧值v = getIntVolatile(o, offset);//如果这个方法的返回值为false,那么继续自旋//如果这个方法的返回值为true,那么自旋结束//o 表示的就是内存值//v 旧值//v + delta 修改后的值} while (!weakCompareAndSetInt(o, offset, v, v + delta));//作用:比较内存中的值,旧值是否相等,如果相等就把修改后的值写到内存中,返回true。表示修改成功。//                                 如果不相等,无法把修改后的值写到内存中,返回false。表示修改失败。//如果修改失败,那么继续自旋。return v;
}

2.9 悲观锁和乐观锁

synchronized和CAS的区别 :

相同点:在多线程情况下,都可以保证共享数据的安全性。

不同点:synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每 次操作共享数据之前,都会上锁。(悲观锁)

cas是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查一下,别人有没有修改过这个数据。

如果别人修改过,那么我再次获取现在最新的值。

如果别人没有修改过,那么我现在直接修改共享数据的值.(乐观锁)

相关文章:

Java之原子性问题的解决

2. 原子性 2.1 volatile-问题 代码分析 : package com.itheima.myvolatile; ​ public class Demo {public static void main(String[] args) {MyThread1 t1 new MyThread1();t1.setName("小路同学");t1.start(); ​MyThread2 t2 new MyThread2();t2.setName(&q…...

实时目标检测:基于YOLOv3和OpenCV的摄像头应用

一、前言 随着人工智能和计算机视觉技术的不断发展,目标检测成为了智能监控、自动驾驶、机器人等领域的关键技术之一。实时目标检测更是对系统的反应速度和准确度提出了更高的要求。本文介绍使用OpenCV和YOLOv3实现实时目标检测的方法,演示如何使用OpenCV调用YOLOv3模型进行…...

【软考】4.2 关系代数

《 关系代数 》 表和表之间的逻辑运算 笛卡尔积&#xff1a;S1 x S2 投影&#xff1a;π&#xff1b;选择某一列&#xff08;属性&#xff09;&#xff1b;一个关系R的投影操作结果也是一个关系&#xff0c;记作Πa&#xff0c;它由从关系R中选出的A列元素构成&#xff1b;选择…...

STM32F4学习笔记读取芯片UID和MAC地址

一、简介 在嵌入式设备开发过程中有时会需要为设备设置唯一的ID用以标识设备唯一&#xff0c;比如要求同一总线上的所有设备ID不能重复&#xff0c;要求设备具体唯一的MAC地址等等。每个STM32微控制器都自带一个96位的唯一ID&#xff0c;这个ID在任何情况下都是唯一且不允许修…...

webpack优化策略

这三点是webpack优化策略的一部分&#xff0c;具体解释如下&#xff1a; 优化正则匹配&#xff08;Test&#xff09;&#xff1a;在webpack的配置中&#xff0c;test属性是一个正则表达式&#xff0c;用于匹配需要应用该loader的文件的扩展名。在您提供的代码中&#xff0c;te…...

讲讲项目里的仪表盘编辑器(三)布局组件

布局容器处理 看完前面两章的讲解&#xff0c;我们对仪表盘系统有了一个大概的理解。接着我们讲讲更深入的应用。 上文讲解的编辑器只是局限于平铺的组件集。而在编辑器中&#xff0c;还会有一种组件是布局容器。它允许其他组件拖拽进入在里面形成自己的一套布局。典型的有分页…...

Linux- 后台运行符、nohup、disown

& &在Unix-like的操作系统&#xff08;如Linux和macOS&#xff09;的shell中&#xff0c;特别是在Bash这样的shell中&#xff0c;经常用作后台运行符号。让我们深入了解一下其功能和用法。 &作为后台运行符号&#xff1a; 基本用法: 当我们在一个命令或者一组命令…...

开发过程教学——交友小程序

交友小程序 1. 我的基本信息2. 我的人脉2.1 我的关注2.2 我的粉丝 3. 我的视频4. 我的相册 特别注意&#xff1a;由于小程序分包限制2M以内&#xff0c;所以要注意图片和视频的处理。 1. 我的基本信息 数据库表&#xff1a; 我的基本信息我的登录退出记录我的登录状态&#x…...

正则表达式 Regular Expression学习

该文章内容为以下视频的学习笔记&#xff1a; 10分钟快速掌握正则表达式_哔哩哔哩_bilibili正则表达式在线测试工具&#xff1a;https://regex101.com/, 视频播放量 441829、弹幕量 1076、点赞数 19330、投硬币枚数 13662、收藏人数 26242、转发人数 2768, 视频作者 奇乐编程学…...

代谢组学最常用到的数据分析方法(五)

代谢组学是一门对某一生物或细胞所有低分子质量代谢产物&#xff08;以相对分子质量<1000的有机和无机的代谢物为研究核心区&#xff09;进行分析的新兴学科。因此从复杂的代谢组学数据中确定与所研究的现象有关的代谢物&#xff0c;筛选出候选生物标记物成为代谢物组学研究…...

105.从前序与中序遍历序列构造二叉树

力扣题目链接(opens new window) 根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如&#xff0c;给出 前序遍历 preorder [3,9,20,15,7] 中序遍历 inorder [9,3,15,20,7] 返回如下的二叉树&#xff1a; class Solution { public:Tr…...

分支定界、分支切割、分支定价的区别

目录 1.从原理的角度 &#xff08;1&#xff09;分支定界&#xff1a; &#xff08;2&#xff09;分支切割&#xff1a; &#xff08;3&#xff09;分支定价&#xff1a; 2.从分支树的角度 &#xff08;1&#xff09;分支定界 &#xff08;2&#xff09;分支切割 &…...

数字IC前端学习笔记:数字乘法器的优化设计(阵列乘法器)

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 数字信号处理作为微处理器的核心部件&#xff0c;是决定着总体处理器性能的因素之一&#xff0c;而数字乘法器是最常见的一种数字信号处理电路。通常情况下&#…...

批量删除wordpress文章修订版本/自动草稿残留数据(3种方法)及四种方法禁用WordPress文章历史修订/自动保存/自动草稿功能

目录 1、批量删除wordpress文章修订版本/自动草稿残留数据&#xff08;3种方法&#xff09; 方法一&#xff1a;SQL命令批量删除 命令&#xff1a; 方法二&#xff1a;利用PHP代码来删除 方法三&#xff1a;利用数据库清理优化插件 WP Clean Up 或 WP Cleaner 批量删除 2…...

HTTP初识,fiddler的使用,URL各部分介绍,QueryString

目录 一、什么是HTTP 二、抓包工具 三、请求的首行 URL 四、URL的各部分详细介绍 一、什么是HTTP 现在网页上&#xff0c;我们常见的是https,但是在二十年前是以http为主&#xff0c;这个协议也叫超文本传输协议&#xff0c;文本->字符串&#xff0c;“超文本”->图片…...

计算机毕业设计 基于SpringBoot的图书馆管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…...

第三章:最新版零基础学习 PYTHON 教程(第十二节 - Python 运算符—Python 中的运算符函数 - 套装1)

Python 在“operator”模块下预定义了许多数学、逻辑、关系、位等运算的函数。本文介绍了一些基本功能。 1. add(a, b):- 该函数返回给定参数的加法。 操作-a +b。 2. sub(a, b):- 该函数返回给定参数的差值。 操作-a -b。 3. mul(a, b):- 该函数返回给定参数的乘积。 操…...

AAD基础知识(identity/token/PRT)

简介 AAD(Azure Active Directory/Azure AD)是微软基于云身份验证和访问控制的解决方案&#xff0c;通过SSO登录其他o365应用(word/outlook/teams…) 微软在2023年7月把AAD重命名为Microsoft Entra ID&#xff0c;官网&#xff1a;https://www.microsoft.com/zh-cn/security/b…...

基于SSM的视频点播系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…...

React 知识点总结

本篇文章是我自己总结已经写过的react知识点&#xff0c;大框架已生成&#xff0c;知识持续更新中。仅供参考 生命周期 React 生命周期 组件基础 react中受控组件与非受控组件 React Portals 理解React页面渲染原理&#xff0c;如何优化React性能&#xff1f; 学习篇之R…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

云原生时代的系统设计:架构转型的战略支点

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、云原生的崛起&#xff1a;技术趋势与现实需求的交汇 随着企业业务的互联网化、全球化、智能化持续加深&#xff0c;传统的 I…...

TI德州仪器TPS3103K33DBVR低功耗电压监控器IC电源管理芯片详细解析

1. 基本介绍 TPS3103K33DBVR 是 德州仪器&#xff08;Texas Instruments, TI&#xff09; 推出的一款 低功耗电压监控器&#xff08;Supervisor IC&#xff09;&#xff0c;属于 电源管理芯片&#xff08;PMIC&#xff09; 类别&#xff0c;主要用于 系统复位和电压监测。 2. …...