线程安全☞原子性

news2024/11/15 18:36:46

何为原子性?

原子性:一条线程在执行一系列程序指令操作时,该线程不可中断。一旦出现中断,那么就可能会导致程序执行前后的结果不一致。与数据库中的原子性(事务管理体现)是相同的

概括:一段程序只能由一条线程去完整的执行,不能被多个线程干扰执行

以最经典的转账为例,甲向乙的账户转账500这个转账行为就包含了两个操作:分别是1. 甲的账户-500 2. 乙的账户 +500但如果此时不能保证原子性操作就可能会出现甲的账户减了500但是乙的账户没有加500的情况

首先我们先来区分哪些是原子操作哪些非原子操作:

int a = 1; // (1)
int b = a; // (2) 
a += b;    // (3)

(1)一个操作,就是将值“1” 赋给 变量a
(2)两个操作,首先获取a的值,然后将a的值赋给b
(3)四个操作,首先获取b的值,再获取a的值,再将a与b的值相加,再将相加后的值赋给a
所以只有(1)和(3)属于原子操作,(2)不构成原子操作

上述举例,我们清楚了原子操作就是一个不可分割的操作。

下面我们来看线程安全的原子性示例:

测试代码:

public class Demo{
    public static void main(String[] args) {
        Temp task = new Temp();
        // 启动100条线程
        for (int i = 1; i <= 100 ; i++) {
            new Thread(task).start();
        }
    }
}
class Temp implements Runnable{
    private int count = 0;
    @Override
    public void run() {
        // 线程任务:将count
        for (int i = 1; i <= 100; i++) {
            count++;
            System.out.println(Thread.currentThread().getName()+"::count====>>"+count);
        }
    }
}

在这里插入图片描述

上述代码实现将Count从0加到10000,每一条线程控制count加100,100条线程启动执行实现。但是在执行过程中会发现会出现几次无法达到10000的结果,产生的原因就是假设当某一条线程在执行count累加时执行到了count=97时(也就是该线程执行失败,并且提交了执行失败的结果)cpu被其他线程拿到,那么其他线程继续拿到count=97进行累加,这样就导致最后结果不准确这个时候线程就不是安全的,也就是说我们这段程序是不具备原子性的。

但是有时效果不是很明显,建议可以将线程数增加到500条;

那么原子性的问题如何解决呢?

解决方法

  1. 加锁–> 悲观锁(阻塞同步)

利用synchronized修饰的同步方法、同步代码块啥的,随便你怎么上锁都可以,本质上的解决机理就是保证线程任务同时只能被一条线程执行,在执行完毕之前其他线程无法拿到执行权。由此来保证线程的原子性

从时间维度上来讲,某一时刻只允许一条线程执行线程任务,其他线程就处于阻塞状态,这种利用阻塞其他线程的方式就称为阻塞同步也叫做互斥同步。大大降低了执行效率和性能

这种解决方式采用了悲观的并发策略,synchronized也被称为悲观锁,为什么说是悲观?因为程序在加锁之初就默认每一次线程操作共享资源时都会被其他线程干扰。即在不进行同步干预的情况下,程序默认每次线程操作共享资源都会存在其他线程的竞争继而导致程序执行出现问题。阻塞同步也就是做出了最坏的打算,故称为悲观锁

  1. 使用原子类( Atomic )–> 乐观锁(非阻塞同步)

为什么还要使用原子类?
because 利用synchronized加锁去解决原子性问题,性能太低了。如果我们的程序线程数量太大,那每次线程任务只能被单条线程执行,效率太低了

原子类是性能高效、线程安全。并且Java提供了比较全面的原子类供开发者使用,下面以AtomicInteger为例来说它的方法使用,多数原子类的方法都有些类似

// 导包
import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger整型原子类

// 常用方法
public final int set(int newValue) //为当前对象赋值
public final int get() //获取当前值
public final int getAndSet(int newValue)//获取当前值然后重新赋值
public final int getAndIncrement()//先获取当前值然后自增
public final int getAndDecrement() //先获取当前值然后自减
public final int getAndAdd(int delta) //获取当前值然后加上delta
boolean compareAndSet(int expect, int update) //如果当前值等于预期值,则以原子方式将该值设置为输入值(update)

除了整型原子类之外还有如长整型原子类AtomicLong、布尔原子类AtomicBoolean、整型数组AtomicIntegerArray、长整型数组AtomicLongArray、引用数据类型数组AtomicReferenceArray等

CAS机制(Compare And Swap)

什么是CAS?

CAS( Compare And Swap )意为比较并交换,CAS的实现机制就是利用了非阻塞同步。

采用乐观的并发策略,当程序运行中,我们不再利用阻塞其他线程来保证当前线程正确执行的方式,而是作出最优情况的解决方案,故而也被称为乐观锁。

即每一次线程操作共享资源都会执行成功并提交正确的结果,但是在将最新的结果提交时,需要与当前存储的值进行比较就是进行一个新值与原值冲突检测,比较完之后如果发现最新结果与当前值一致则说明执行失败,反之则是执行成功然后替换到原值即可

我们依然用上述操作count作为示例,如下图所示

在这里插入图片描述

可以看出,不论任何一条线程正在操作count,都可以与其他线程进行竞争。非阻塞同步大大的节省了线程阻塞和唤醒的性能开销

下面谈一下CAS具体的实现机制(CAS算法

CAS实现主要用到三个操作数,分别是 内存地址S、原值(预期值)A、新值B

当线程向主内存中提交一个共享变量的新值B时,首先会将原值A与S处存储的值进行比较,只有当两个值相同时(没有其他线程干扰),才能将新值B提交至主内存更新共享变量

CAS算法真正关注的是线程提交时的S处值与预期值是否相同,但仍然存在ABA漏洞,暂时不做深究

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go语言设计与实现 -- 浅谈垃圾回收机制

概述 GC就是垃圾回收机制。而我们知道&#xff0c;内存区域是分成几个块儿的&#xff0c;例如&#xff1a; 堆区&#xff1a;为对象分配内存空间&#xff0c;在栈区和bss区之间存放函数参数&#xff0c;返回值&#xff0c;局部变量全局区&#xff1a;常量区&#xff08;const…

第一批“阳康”涌向三亚,最大的赢家或是携程

在2022年接近尾声时&#xff0c;给居民出行带来近三年困扰的疫情防控&#xff0c;终于迎来了好消息。随着国家“新十条”防控新政策的出台以及优化&#xff0c;过去出门不便、频繁查验核酸码的时代一去不返。尤其是各地逐步放宽出入境管控政策后&#xff0c;在线旅游行业也迎来…

PMP的价值有哪些?

我个人认为&#xff0c;考证只有两个出发点是正确的。一是为了提升自己或者满足自己的兴趣&#xff0c;另一个是和自己的职业规划相关。 比如&#xff0c;有同学想提升自己英语能力&#xff0c;可以考四六级&#xff0c;或者更厉害一点的考雅思、托福。比如&#xff0c;有的同…

电脑自动关机是什么原因?4个解决方法,赶紧码住收藏!

正在使用电脑&#xff0c;突然自动关机。如果没有及时保存好资料&#xff0c;我们辛辛苦苦写的资料就会付诸东流。电脑自动关机是什么原因&#xff1f;其实主要是以下这4个方面的原因。你可以根据下面不同的原因来对症下药&#xff0c;寻找解决电脑自动关机的最好方法&#xff…

直播弹幕系统(七)- 利用动态创建队列完成直播间独立聊天

直播弹幕系统&#xff08;七&#xff09;- 利用动态创建队列完成直播间独立聊天前言一. 动态创建队列1.1 测试 - 动态创建队列1.2 测试 - 聊天室独立前言 上一篇 SpringBoot STOMP RabbitMQ&#xff08;使用MQ替代Spring代理&#xff09; 中主要讲解了如何整合STOMP以及Rabb…

Vue条件语句中v-if、v-else、v-else-if的用法

文章目录1、v-if和v-else结合使用1.1 出现的错误2、v-if、v-else-if和v-else的联合使用2.1 出现的错误3、如果想要同时切换多个元素3.1 效果展示1、v-if和v-else结合使用 v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面&#xff0c;否则它将不会被识别。 <span…

智能指针(二) shared_ptr 注意点

智能指针(二) shared_ptr 注意点 1 不存在 int * 到 shared_ptr 的隐式类型转换 void proc(shared_ptr<int> ptr) {cout << "ptr.use_count()" << ptr.use_count() << endl;cout << "调用成功" << endl;return; }in…

独立产品灵感周刊 DecoHack #043 - 互联网从业者的灵感数据库

本周刊记录有趣好玩的独立产品设计开发相关内容&#xff0c;每周发布&#xff0c;往期内容同样精彩&#xff0c;感兴趣的伙伴可以点击订阅我的周刊。为保证每期都能收到&#xff0c;建议邮件订阅。欢迎通过 Twitter 私信推荐或投稿。很完美的断更了2期&#xff0c;有一期是因为…

RFID技术和NFC技术的原理及区别,你都了解吗?

物联网是信息技术发展的重要推动力&#xff0c;推动了农业、工业、制造业、服务业等多个行业的发展&#xff0c;物联网主要由三个关键技术组成&#xff0c;即连接、物体标识和数据传输&#xff0c;人们把RFID技术作为物体标识的代表&#xff0c;随着技术的进步起源于RFID技术的…

谷粒商城-基础篇-Day07-品牌分类关联与级联更新

将品牌分类和品牌名称的关系放在pms_category_brand_relation表中 获取该列表品牌所有的关联信息 /*** 列表*/GetMapping("/catelog/list")public R list(RequestParam("brandId") Long brandId){List<CategoryBrandRelationEntity> datacategoryBra…

Java日期时间类

Java日期时间类Datenew Date()**获取当前系统时间**通过**指定毫秒数得到时间**format**指定日期格式**SimpleDateFormat的模式字母&#xff1a;parse()可以把**格式化的String转成对应Date**Calendar&#xff08;日历&#xff09;创建日期类对象获取日历对象的某个日历字段第三…

【Linux】五、Linux 进程控制(总)|进程创建|进程终止|进程等待进程程序替换|模拟shell

目录 一、进程创建 1.1 再谈 fork 函数 1.2 fork 函数返回值问题 1.2 写时拷贝 1.3 fork 常规用法 1.4 fork调用失败的原因 二、进程终止 2.1 进程退出码 2.2 进程退出场景 2.3 进程如何退出 三、进程等待 3.1 进程等待必要性 3.2 进程等待的方法 3.2.1 通过 wai…

【二进制安全面试题】linux篇:保护机制、函数调用约定

前言 上来先道歉&#xff0c;对不起(&#xff1e;人&#xff1c;&#xff1b;)对不起&#xff0c;博客鸽了好久。私下有好多朋友问我毕业工作的事情&#xff0c;毕竟搞二进制最重要的是要有热情&#xff01;我能做的也是有限&#xff0c;每个人的学习方式不完全相同&#xff0c…

Http4s 存在输入验证不当漏洞(CVE-2023-22465)

漏洞描述 http4s 是一个用于处理 HTTP 服务的 Scala 接口。 http4s 的受影响版本延迟加载模型化标头&#xff08;modeled headers&#xff09;&#xff0c;用于处理规范化标头的请求&#xff08;如&#xff1a;Option[Header] req.headers.get(“User-Agent”.ci)&#xff0…

C语言进阶——字符串函数(一)

目录 一. strlen 二. strcpy 三. strcat 四. strcmp 五. strncpy 六. strncat 七. strncmp 八. strstr 九. strtok 一. strlen 字符串以 \0 作为结束标志&#xff0c;strlen函数返回的是在字符串中 \0 前面出现的字符个数&#xff08;不包 含 \0 …

陪诊软件开发,陪诊服务具备哪些好处,前景如何

在当下互联网快速发展的时代&#xff0c;我们要首先明确&#xff0c;一个行业的发展最重要的是什么&#xff0c;什么才能促进这个行业的前进。当然是用户的数量&#xff0c;**而我们的陪诊服务&#xff0c;潜在的用户数量是巨大的。因为自己独立不便就医的人群&#xff0c;都可…

maven导入第三方jar包,出现找不到类

我们开发时&#xff0c;会用到第三代第三方的jar包&#xff0c;私服上没有&#xff0c;只能导入使用。 导入步骤&#xff1a; 1、在项目根目录建文件夹lib&#xff0c;降jar包复制过去。 在pom.xml中引入jar包&#xff0c;如引入bcprov-jdk15on-1.59.jar <dependency>&…

c++ - 第21节 - 智能指针

1.为什么需要智能指针 分析一下下面这段程序有没有什么内存方面的问题&#xff1f;前面在异常的博客中&#xff0c;我们分析了下图一的代码Func函数中如果div()函数抛异常则程序会直接跳到主函数的catch捕获程序部分&#xff0c;然后接着主函数catch捕获程序部分往后执行代码&a…

【IOS的safari浏览器】uniapp的H5项目 safari<添加到主屏幕>功能的实现(多页面、单页面)

uniapp的H5项目safari <添加到主屏幕>功能的实现ios添加到主屏幕的需求具体效果实现前提完整的HTML页面如何判断应用是从主屏幕打开还是从浏览器打开特殊情况ios添加到主屏幕的需求 添加到主屏幕——这个功能属于ios的safari浏览器的特性之一&#xff0c;他可以让我们的…

Java环境安装、替换jdk后java编译javac无反应,但java和java -version可以成功:实操解决方案

这里写自定义目录标题问题背景方案一方案二方案三问题背景 最近换了新电脑&#xff0c;安装java环境&#xff0c;一次性下载了3个jdk版本&#xff0c;在配置后返现 cmd命令行下javac编译java文件不成功&#xff0c;但是输入java和java -version没问题 在CSDN看了许多解决方案…