当前位置: 首页 > 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…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...