Java并发之原子变量及CAS算法-下篇

概述

本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK提供的类中是怎么保证变量原子性的呢?。对应Java中的包是:java.util.concurrent.atomic包下。因为涉及到了CAS算法,需要对CAS算法讲解及CAS算法三个问题怎么解决以及和Synchroized比较。文章比较长,所以就分为上下两个篇幅讲解。本文是上篇《Java并发之原子变量及CAS算法-下篇》

本文是《凯哥分享Java并发编程之J.U.C包讲解》系列教程中的第四篇。如果想系统学习,凯哥(kaigejava)建议从第一篇开始看。

在上一篇中,我们讲解了i++在多线程下变量原子性问题以及怎么解决。在本篇中,我们详细讲解什么是CAS算法?CAS和Synchroized区别是什么?以及CAS算法产生的问题及怎么解决。

CAS简介

什么是CAS算法?

CAS:Compare-And-Swap即比较并交换的意思。

CAS包含了三个操作的数据:

主内存中的变量值:V

预估值(可以理解为原来旧的值):A

更新值(操作后,要更新的值):B

CAS的特点:

当且仅当预估值A=内存值V的时候,才会将V的值更新为B。否则也不操作。

V==A;V=B;

使用CAS算法多线程操作的时候,有且仅有一个线程可以操作成功,其他线程都会操作失败。失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

失败的线程采用自旋来进行尝试的。

我们以AtomicInteger对象中的getAndIncrement()方法为例来看看:

模拟后的源码:

CAS VS Synchroized比较

那么CAS的算法的效率为什么会比Synchronized的效率高呢?

Synchroized是阻塞算法的;而CAS是非阻塞的,采用的是乐观锁技术。

因为阻塞算法是CPU切换的,而CAS是CPU指令操作。CPU切换时间相对于CPU指令操作来说时间更长。所以使用CAS算法的线程比使用Synchroized的效率高。

CAS的优缺点

优点:一种线程同步的解决方案,使用CAS就可以不用加锁来实现线程的安全性。

缺点:

1:只能保证一个共享变量的原子操作;

2:循环时间长,开销很大;

3:会产生ABA问题。

缺点解决方案:

缺点一:

当对一个共享变量操作的时候,可以使用带有自旋(循环)的CAS方法来保证原子性操作,但是如果是多个变量共享的时候,可以封装到对象中或者是使用锁来保证原子性。

缺点二:

如果采用自旋的CAS方式来保证原子性,会一直进行尝试。如果时间太长的话,对CPU来说也会带来很大开销的。

缺点三:ABA问题

何为ABA问题?

如线程A修改共享变量值为A;线程B修改值为B。后来共享变量又被修改成了A,这种情况下CAS算法操作就会误认为共享变量A没有别修改过。这就是CAS算法的“漏洞”。

举个很简单的例子:

解决ABA问题

看到这里大家或许心里会想,我Kao,这不就是一个坑吗?JDK埋下的坑!既然有这个坑,那还敢用吗??淡定,保持淡定点。你能想到的问题,JDK开发者也能想到。所以补救办法就是:

注意:是AtomicStampedReference。虽然和AtomicReference这个类有点像。但是不一样。

查看源码注释:

简单理解,就是这个类添加了一个版本号。,每次操作都对版本号进行自增,那每次CAS不仅要比较value,还要比较stamp,当且仅当两者都相等,才能够进行更新。

具体怎么操作的呢?

在初始化的时候,就定义了pair对象。

在compareAndSet的时候,会对版本号进行比较。如下图:

讲明白了CAS原理之后,我们来修改i++的问题。使其成为保证原子性:

很简单只需要修改两行代码即可:

1:声明变量的时候使用AtomicInteger对象:

private AtomicInteger shardData = new AtomicInteger(0);

new AtomicInteger(0)其中的0可以不用写

2:修改i++的方法:

return shardData.getAndIncrement();

这样就可以了。

总结

Java中保证变量原子性使用的是current.atomic包下的对象来实现的。

如何保证原子性呢?

1:变量都是用volatile关键字修饰后,保证了内存的可见性;

2:使用CAS算法,保证了原子性。

Synchroized VS volatile VS CAS

在上一篇文章中我们知道了Volatile只能保证变量共享变量在内存中的可见性;不互斥;不能保证原子性;

在本篇中,我们知道了CAS是非阻塞的使用乐观锁技术来实现原子性。但是会产生其他问题,不过也可以解决。

Synchroized是阻塞性算法的实现。具有互斥性

凯哥个人博客:www.kaigejava.com

Java并发之原子变量及CAS算法-下篇的更多相关文章

  1. Java多线程-----原子变量和CAS算法

       原子变量      原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题      Java给我们提供了以下几种原子类型: AtomicInteger和Ato ...

  2. 三、原子变量与CAS算法

    原子变量:jdk1.5 后 java.util.concurrent.atomic 包下提供了常用的原子变量: - AtomicBoolean - AtomicInteger - AtomicLong ...

  3. 原子变量与CAS算法

    原子变量 为了引出原子变量这个概念,我们先看一个例子. package com.ccfdod.juc; public class TestAtomicDemo { public static void ...

  4. 原子变量与CAS算法小结

    CAS算法 CAS(compare-and-swap)是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问. CAS是一种无锁非阻塞算法的实现. CAS ...

  5. volatile关键字与内存可见性&原子变量与CAS算法

    1 .volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 2 .原子变量:jdk1.5后java.util.concurrent.atomic 包下提供常用的原子变量 ...

  6. Java并发之原子变量和原子引用与volatile

    我们知道在并发编程中,多个线程共享某个变量或者对象时,必须要进行同步.同步的包含两层作用:1)互斥访问(原子性):2)可见性:也就是多个线程对共享的变量互斥地访问,同时线程对共享变量的修改必须对其他线 ...

  7. 原子变量与CAS算法(二)

    一.锁机制存在的问题 (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2)一个线程持有锁会导致其它所有需要此锁的线程挂起. (3)如果一个优先级高的线程等待一个 ...

  8. Java编程的逻辑 (70) - 原子变量和CAS

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  9. java并发编程学习: 原子变量(CAS)

    先上一段代码: package test; public class Program { public static int i = 0; private static class Next exte ...

  10. 计算机程序的思维逻辑 (70) - 原子变量和CAS

    从本节开始,我们探讨Java并发工具包java.util.concurrent中的内容,本节先介绍最基本的原子变量及其背后的原理和思维. 原子变量 什么是原子变量?为什么需要它们呢? 在理解synch ...

随机推荐

  1. Python数据分析代码示例

    数据清洗 在进行数据分析之前,通常需要对原始数据进行清洗,即处理缺失值.异常值.重复值等问题. 下面是一个数据清洗的示例代码: import pandas as pd # 读取原始数据 data = ...

  2. JSP快速上手与MVC模式和三层架构的知识点总结+综合案例

    阅读提示: 说明 由于JSP实在是太 难读 难写 复杂 占资源 难调试 不分离 了,拉跨!(节目效果哈,勿喷),作为一种有(ji)更(hu)好(jiu)的(yao)上(bei)位(tao)替(tai) ...

  3. Java智能之Spring AI:5分钟打造智能聊天模型的利器

    前言 尽管Python最近成为了编程语言的首选,但是Java在人工智能领域的地位同样不可撼动,得益于强大的Spring框架.随着人工智能技术的快速发展,我们正处于一个创新不断涌现的时代.从智能语音助手 ...

  4. supervisor.conf部署及维护

    启动服务 supervisord -c /etc/supervisord.conf 启动服务 supervisorctl start 关闭服务 supervisorctl stop

  5. [oeasy]python0075_删除变量_del_delete_variable

    删除变量 回忆上次内容 上次我们研究了字节序 字节序有两种   符号 英文名称 中文名称 < little-endian 小字节序 > big-endian 大字节序 字节序 用来 明确 ...

  6. oeasy教您玩转vim - 34 - # 查找进阶

    ​ 查找进阶 回忆上节课内容 上次是搜索,是全文搜索 和我们以前的行内有点像 / 正向,? 反向 n 保持方向,N 改变方向 hls 让搜索结果高亮 wrapscan 可以从头搜索 noh 取消本次高 ...

  7. Day 5 - 双指针与折半搜索

    双指针 本页面将简要介绍双指针. 引入 双指针是一种简单而又灵活的技巧和思想,单独使用可以轻松解决一些特定问题,和其他算法结合也能发挥多样的用处. 双指针顾名思义,就是同时使用两个指针,在序列.链表结 ...

  8. C# 算术表达式求值(后缀法),看这一篇就够了

    一.种类介绍 算术表达式有三种:前缀表达式.中缀表达式和后缀表达式.一般用的是中缀,比如1+1,前后缀就是把操作符移到前面和后面,下面简单介绍一下这三种表达式. 1.前缀表示法 前缀表示法又叫波兰表示 ...

  9. python私有变量和方法

    python私有变量和方法 1,私有变量和私有方法无法被继承 2,私有变量和私有方法可以放在普通方法(实例方法)里面被子类继承 class A: def __init__(self, name): s ...

  10. linux环境部署搭建流程

    linux环境部署搭建流程 1,ubuntu/centos系统安装 2,ip网络配置 3,路由和防火墙 4,时间同步服务器 5,ssh协议配置(Windows安装xshell/secureCRT) 6 ...