惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Troy Hunt's Blog
T
The Exploit Database - CXSecurity.com
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
F
Fortinet All Blogs
博客园_首页
P
Proofpoint News Feed
V
Vulnerabilities – Threatpost
The Cloudflare Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
H
Heimdal Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
A
About on SuperTechFans
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Security Affairs
The Register - Security
The Register - Security
S
Security @ Cisco Blogs
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
Schneier on Security
Schneier on Security
WordPress大学
WordPress大学
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
T
Tailwind CSS Blog
Hacker News: Ask HN
Hacker News: Ask HN
W
WeLiveSecurity
D
Docker
L
LangChain Blog
B
Blog RSS Feed
The Last Watchdog
The Last Watchdog
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
N
Netflix TechBlog - Medium
酷 壳 – CoolShell
酷 壳 – CoolShell
I
InfoQ
The Hacker News
The Hacker News
AWS News Blog
AWS News Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
宝玉的分享
宝玉的分享
I
Intezer
云风的 BLOG
云风的 BLOG
V2EX - 技术
V2EX - 技术
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO

技术改变视野

为程序员、设计师打造的摸鱼社区:摸鱼派! - 技术改变视野 大白话 | Java初学指♂男:“说说初学的误区与死结”( 壹 | 反射与Field ) - 技术改变视野 大白话之耦合性:什么是耦合性和内聚性?用编程语言实例讲解! - 技术改变视野 🎨 适用于 Solo / Bolo 的清新高级主题 —— Sakura | 移植自 WordPress 主题 - 技术改变视野 我在 GitHub 上的开源项目 - 技术改变视野 为IDEA/Git配置GPG密钥,点亮GitHub Verified Commit标识 - 技术改变视野 从 SpringMVC 转为使用 SpringBoot —— 手把手带你使用 Intellij IDEA 最快速地搭建 SpringBoot+MyBatis(无 XML)+Thymeleaf - 技术改变视野 无题 - 技术改变视野 年轻人的第一辆京牌电动车 - 技术改变视野 我们忙碌且漫长的一生,有多久是为自己而活? - 技术改变视野 一个最简单的实例理解Semaphore在Java中的作用 - 技术改变视野 说说《后浪》 - 技术改变视野 离线使用 Google Fonts:让你的网站更精致 | goofoffline 神器,一键下载生成离线字体库! - 技术改变视野 实例带你学会简单的Java Thread多线程 - 技术改变视野 一个实例理解Java的接口(interface)用处与用法 - 技术改变视野 一个实例带你理解JavaBean - 技术改变视野 WebFilter-SpringBoot过滤器注解实例讲解 - 技术改变视野 实例带你搞懂Java多线程&&线程池之(壹):线程池与多线程的关系和区别 - 技术改变视野 实例带你搞懂Java多线程&&线程池之(贰):简单的线程池应用 - 技术改变视野 实例带你获取多线程Thread的返回值之 (壹) - Callable的运行 - 技术改变视野 实例带你获取多线程Thread的返回值之 (贰) - Callable配合线程池返回数据 - 技术改变视野 一个实例理解Java Runnable多线程用处与用法 - 技术改变视野 从零开始OpenSSL之 (壹) - 使用genrsa、rsa、rsautl生成公私钥 - 技术改变视野 从零开始 OpenSSL 之 (贰) - 使用 rsautl 解密文件 - 技术改变视野 大白话之Docker(壹):快速入门&&简单官方实例 - 技术改变视野 大白话之Docker(贰):简单部署一个Tomcat服务并发布内容 - 技术改变视野 大白话之从零讲解DVWA(壹)-SQL注入(SQL Injection) Low Level - 技术改变视野 大白话之从零讲解DVWA(贰)-SQL注入(SQL Injection) Medium/High Level - 技术改变视野 Java日志插件-Slog4J下载 - 技术改变视野 大白话之必会Java Atomic | 线程一点也不安全(一):比自增和synchronized更快速、靠谱的原子操作(调用C语言) - 技术改变视野 Log-MySQL root用户登录后无法查看数据库全部表/正常访问数据库 Access denied for user 'root'@'localhost' to database - 技术改变视野 大白话之Docker(叁):制作一个运行Tomcat服务端的Docker镜像 - 技术改变视野 Github仅保留指定文件/文件夹当前Commit,删除所有历史记录,清除「敏感信息」 - 技术改变视野 将Tomcat、MySQL从Linux迁移到Windows的心路历程(干货):令人恐惧的字符编码 - 技术改变视野 Spring: java.lang.NoSuchMethodError: clearCache | ClassNotFoundException | Error during artifact deployment 思路及解决办法 - 技术改变视野 大白话之Java反射-初学最迷的概念:能干啥?咋用? - 技术改变视野 大白话之Java面向接口编程:最“正经”的中文实例讲解,看不懂来打我! - 技术改变视野 大白话 | 课堂实践:使用Iterator对数组进行遍历 - 技术改变视野 大白话之fail-fast | fail-safe:为什么会有这个机制?它有什么作用? - 技术改变视野 刨根问底 | 大白话:在使用注解后,框架是怎么知道你哪个方法使用了注解的?用@RequestMapping注解举例详解! - 技术改变视野 随笔 | 奇淫技巧 | Java:记return和短路运算符的妙用 - 技术改变视野 随笔 | Tomcat:续-从Linux迁移到Windows编码问题彻底解决 - 技术改变视野 大白话之AutoClosable接口 | try-with-resources:JDK1.7的新特性,提高重复回收效率 - 技术改变视野 大白话之Java多线程join方法:开局一张图,试学一分钟,你就费和我一样,理解介个方法 - 技术改变视野 大白话之Java Stream流:将类数组流化,便捷批量修改,通俗讲解! - 技术改变视野 Java 困扰三周の问题:使用byte[]或skip()方法读取字节流Stream文件尾部多/少/缺字节解决方法 - 技术改变视野 大白话之Java级联调用:一个类,一条语句,同时调用好几个方法,串成一串 - 技术改变视野
大白话之必会Java Atomic | 线程一点也不安全(二):Atomic的ABA问题会导致什么情况?如何解决? - 技术改变视野
adlered · 2019-12-09 · via 技术改变视野

前言

第一章还没看过?点我可以穿越

阅读本篇文章,你需要了解以下知识:

  • Atomic是什么?(点此跳转
  • 单向链表的原理

从上一章的内容,我们可以了解到,Atomic可以基本解决线程同步安全的问题。而本章我们将讨论Atomic的缺点与它的原子性。

ABA问题

什么是ABA问题?首先我们都知道,AtomicCAS模型,会先读取变量的值,作为预期旧值,然后再基于旧值产生操作生成新值,再确认变量是否为预期旧值,如果是,修改为新值。

我们以单向链表来演示ABA会导致的问题:

2.png

解决ABA问题

现在我们知道了,由于Atomic仅判断了旧值,但并没有意识到整个链表已经被修改过一次了。所以我们要引入一个新的概念:

版本

Atomic在修改值时,保存的不仅再是旧值,还有一个版本号。在每次更改后,版本号都会变化,这样就不会再产生ABA问题了。我们看图:

1.png

AtomicStampedReference

Atomic的开发者自然也意识到了这个问题,并后续开发了AtomicStampedReference来修复这个问题。我们用一段简单的代码来实现:

 1import java.util.concurrent.atomic.AtomicStampedReference;
 2
 3public class Main {
 4    public static void main(String[] args) {
 5        /*
 6        实例化有版本标记的Atomic类
 7        传参1:初始化版本
 8        传参2:初始化值
 9         */
10        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(1, 66);
11        /*
12        打印值
13        getStamp()方法获取当前值
14         */
15        System.out.println("当前值:" + atomicStampedReference.getStamp() + " 当前版本:" + atomicStampedReference.getReference());
16        /*
17        使用compareAndSet(V expectedReference, V newReference, int expectedStamp,mint newStamp)方法修改值
18        传参1:预期中的版本
19        传参2:如果修改时预期中的版本和旧值正确,则修改为指定版本
20        传参3:预期中的旧值
21        传参4:如果修改时预期中的版本和旧值正确,则修改为指定值
22         */
23        System.out.println(
24                atomicStampedReference.compareAndSet(
25                1,
26                2,
27                atomicStampedReference.getStamp(),
28                atomicStampedReference.getStamp() + 22
29                 )
30        );
31        //再次打印值
32        System.out.println("当前值:" + atomicStampedReference.getStamp() + " 当前版本:" + atomicStampedReference.getReference());
33    }
34}

得到结果:

1当前值:66 当前版本:1
2true
3当前值:88 当前版本:2

实现源码(选读)

让我们来看看,我们用来修改值的compareAndSet()方法是如何实现的:

默认构造方法

 1    /**
 2     * Creates a new {@code AtomicStampedReference} with the given
 3     * initial values.
 4     *
 5     * @param initialRef the initial reference
 6     * @param initialStamp the initial stamp
 7     */
 8    public AtomicStampedReference(V initialRef, int initialStamp) {
 9	//生成新的集合并存储
10        pair = Pair.of(initialRef, initialStamp);
11    }

当我们实例化AtomicStampedReference时,这段代码会执行。Pair是一个集合,用于存储预期值预期版本

compareAndSet

 1    /**
 2     * Atomically sets the value of both the reference and stamp
 3     * to the given update values if the
 4     * current reference is {@code ==} to the expected reference
 5     * and the current stamp is equal to the expected stamp.
 6     *
 7     * @param expectedReference the expected value of the reference
 8     * @param newReference the new value for the reference
 9     * @param expectedStamp the expected value of the stamp
10     * @param newStamp the new value for the stamp
11     * @return {@code true} if successful
12     */
13    public boolean compareAndSet(V   expectedReference,
14                                 V   newReference,
15                                 int expectedStamp,
16                                 int newStamp) {
17	//生成一个新的集合,用于和存储的集合对比
18        Pair<V> current = pair;
19        return
20            expectedReference == current.reference &&
21            expectedStamp == current.stamp &&
22            //短路与,如果上方存储的预期值相等,则执行下方内容(赋予新值和新版本),并返回true
23            ((newReference == current.reference &&
24              newStamp == current.stamp) ||
25		//如果修改失败,则使用CAS
26             casPair(current, Pair.of(newReference, newStamp)));
27    }

后语

JDK5版本开始,新增了AtomicStampedReference,它能利用版本戳很好地解决ABA问题

但相对的,AtomicStampedReference可能会对内存空间和性能产生一些小的影响,当大量线程访问相同的原子值时,性能会大幅下降。所以JDK8增加了LongAdderLongAccumulator类以解决这个问题。

至于Atomic拥有原子性,原因是Atomic修改值的过程非常严谨,不会被打断,所以总能得到预期的值。