深入掌握底层源码常见的 CAS 原子编程

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果要聊原子类相关的话题, 可以先从基本的概念开始","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1、原子类为了解决什么样的问题?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答: 为了解决并发场景下无锁的方式保证单一变量的数据一致性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2、什么情况下存在并发问题?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"答: 多个线程同时读写同一个共享数据时存在多线程并发问题","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解决并发安全问题的方式有很多种方式, ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"著名的就是 JDK 并发包 concurrent","attrs":{}},{"type":"text","text":", 为了并发而存在的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"阅读文章收获如下知识:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 无锁编程产出背景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - CAS 如何实现的无锁编程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - CAS 使用中的 “ABA” 痛点","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 如何解决 “ABA” 问题","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文章首发自公众号 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"【源码兴趣圈】","attrs":{}},{"type":"text","text":",关注公众号第一时间获取后端硬核知识,已发布 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"【44】篇","attrs":{}},{"type":"text","text":" 原创技术博文","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"非原子计算","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家应该都知道, 类似于代码中的 i++ 操作, 虽然是一行, 但是执行时候是分为三步的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从主存获取变量 i","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"变量i值+1","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新增后变量i值写回主存","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"写个小程序来更好的理解, 不论我们怎么运行下方程序, 99% 以上概率不会到达 100000000","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static int NUM = 0;\npublic static void main(String[] args) throws InterruptedException {\n for (int i = 0; i < 10000; i++) {\n new Thread(() -> {\n for (int j = 0; j < 10000; j++) {\n NUM++;\n }\n }).start();\n }\n Thread.sleep(2000);\n System.out.println(NUM);\n /**\n * 99149419\n */\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以使用 JDK 自带的 synchronized, 通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"互斥锁的方式","attrs":{}},{"type":"text","text":" 同步执行 NUM++ 这个代码块","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static int NUM = 0;\npublic static void main(String[] args) throws InterruptedException {\n for (int i = 0; i < 10000; i++) {\n new Thread(() -> {\n for (int j = 0; j < 10000; j++) {\n synchronized (Object.class) {\n NUM++;\n }\n }\n }).start();\n }\n Thread.sleep(2000);\n System.out.println(NUM);\n /**\n * 100000000\n */\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面一直在说锁, 写着 AtomicInteger 在这说锁的问题, 咱不能挂着羊皮卖狗肉是吧","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/87/87f97e887abc587d82d342812d314941.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果看过 JDK 源码 JUC 包下的类库源代码, 关于 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Atomic 开头的类库","attrs":{}},{"type":"text","text":" 应该不会陌生","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Atomic 英 [əˈtɒmɪk] 美 [əˈtɑːmɪk] 翻译 : 原子的","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果不使用锁来解决上面的非原子自增问题, 可以这么来写","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"static AtomicInteger NUM = new AtomicInteger();\npublic static void main(String[] args) throws InterruptedException {\n for (int i = 0; i < 10000; i++) {\n new Thread(() -> {\n for (int j = 0; j < 10000; j++) {\n \t// 🚩 重点哦, 自增并获取新值\n NUM.incrementAndGet();\n }\n }).start();\n }\n Thread.sleep(2000);\n System.out.println(NUM);\n /**\n * 100000000\n */\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"AtomicInteger 简介","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到一个技术点或者框架之类比较新颖的知识, 一般会思考两个问题","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"它是什么","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AtomicInteger 是 JDK 并发包下提供的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"操作 Integer 类型原子类","attrs":{}},{"type":"text","text":", 通过调用底层 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Unsafe 的 CAS 相关方法实现原子操作","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于乐观锁的思想实现的一种无锁化原子操作, ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"保障了多线程情况下单一变量的线程安全问题","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于 CAS 的概念下面会详细说明","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"有什么优点","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AtmoicInteger 使用硬件级别的指令 CAS 来更新计数器的值, 机器直接支持的指令, ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"这样可以避免加锁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如像互斥锁 synchronized 在并发比较严重情况下, 会将锁 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"升级到重量级锁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"唤醒与阻塞线程时会有一个 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用户态到内核态的一个转变","attrs":{}},{"type":"text","text":", 而转换状态是需要消耗很多时间的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"写过程序进行测试, 尽管 synchronized 经过升级后, 性能有了大幅度提升, 但在 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"一般并发场景下, CAS 无锁算法性能更高一些","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然不可能会有尽善尽美的存在, 关于 CAS 无锁算法会在下面说明劣势所在","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"结构分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AtomicInteger 有两个构造方法, 分别是一个无参构造及有参构造","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"无参构造的 value 就是 int 的默认值 0","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有参构造会将 value 赋值","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public AtomicInteger() { }\n\npublic AtomicInteger(int initialValue) {\n value = initialValue;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AtomicInteger 有三个重要的变量, 分别是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Unsafe:","attrs":{}},{"type":"text","text":" 可以理解它对于 Java 而言, 是一个 \"BUG\" 的存在, 在 AtomicInteger 里的最大作用就是直接操作内存进行值替换","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"value:","attrs":{}},{"type":"text","text":" 使用 int 类型存储 AtomicInteger 计算的值, 通过 volatile 进行修饰, ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"提供了内存可见性及防止指令重排序","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"valueOffset:","attrs":{}},{"type":"text","text":" value 的内存偏移量","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 获取Unsafe实例\nprivate static final Unsafe unsafe = Unsafe.getUnsafe();\nprivate static final long valueOffset;\n\n// 静态代码块,在类加载时运行\nstatic {\n try {\n \t// 获取 value 的内存偏移量\n valueOffset = unsafe.objectFieldOffset\n (AtomicInteger.class.getDeclaredField(\"value\"));\n } catch (Exception ex) {\n throw new Error(ex);\n }\n}\n\nprivate volatile int value;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里罗列出一些常用API, 核心实现思路都是一致的, 会着重讲其中一个","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 获取当前 value 值\npublic final int get();\n\n// 取当前的值, 并设置新的值\npublic final int getAndSet(int newValue);\n\n// 获取当前的值, 并加上预期的值\npublic final int getAndAdd(int delta);\n\n// 获取当前值, 并进行自增1\npublic final int getAndIncrement();\n \n// 获取当前值, 并进行自减1\npublic final int getAndDecrement();","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"获取当前值并自增 #getAndIncrement()","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看一下具体在源码中是如何操作的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public final int getAndIncrement() {\n \treturn unsafe.getAndAddInt(this, valueOffset, 1);\n}\n\n/**\n * unsafe.getAndAddInt\n *\n * @param var1 AtomicInteger 对象\n * @param var2 value 内存偏移量\n * @param var4 增加的值, 比如在原有值上 + 1\n * @return\n */\npublic final int getAndAddInt(Object var1, long var2, int var4) {\n int var5;\n do {\n // 内存中 value 最新值\n var5 = this.getIntVolatile(var1, var2);\n } while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));\n\n return var5;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里就是 CAS 体现无锁算法的地方, 先来说下这段代码的执行步骤","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、根据 AtomicInteger 对象 以及 value 内存偏移量获取对应 value 最新值","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、通过 compareAndSwapInt(...) 将内存中的值(var5)更改为期望的值 (var5+var4), 不存在多线程竞争成功修改返回 True 结束循环, Flase 继续执行循环","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"精髓的地方就在于 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"compareAndSwapInt(...)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * 比较 var1 的 var2 内存偏移量处的值是否和 var4 相等, 相等则更新为 var5\n *\n * @param var1 AtomicInteger 对象\n * @param var2 value 内存偏移量\n * @param var4 value 原本的值\n * @param var5 期望将 value 设置的值\n * @return\n */\npublic final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于是 native 关键字修饰, 我们无法查看其源码, 说明一下方法思路","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、通过 var1(AtomicInteger) 获取到 var2 (内存偏移量) 的 value 值","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、将 value(内存中值) 与 var4(线程内获取的value值) 进行比较","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、如果相等将 var5(期望值) 设置为内存中新的 value 并返回 True","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4、不相等返回 False 继续尝试执行循环","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"图文分析 CAS","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里给出一组图片进一步理解 CAS","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Unsafe#getAndAddInt()","attrs":{}},{"type":"text","text":" 以此方法为例","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、这个图片相当于 AtomicInteger 对象对应的 valueOffset 位置","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/27/27e1023ac7021b621f8f2e2df68e4dfa.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、线程一执行 var5 = this.getIntVolatile(var1, var2)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b0/b056a64fdcaee8a583887c223ed58124.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、线程二执行 var5 = this.getIntVolatile(var1, var2)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此时在线程一、二的工作内存中 var5 都是 0","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c6/c63bbd511b5ff20ddfed1182f69fd0f5.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4、线程一欲修改内存中的 value 为1, 通过比对 var4 与内存中的值相等, 内存值成功被设置成 1","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/67/676a2d6ed5c96a1d45e2a1a662ed81d9.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5、线程二也是要修改内存中对应的 value 值, 发现已经不相等了, 返回 False 继续尝试修改","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d5/d5a3b09b2372a7a8300735ff018e4152.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"不足之处","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CAS 虽然能够实现无锁编程, 在一般情况下对性能做出了提升, 但是并不是没有局限性或缺点","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1、CPU 自旋开销较大","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在高并发情况下, 自旋 CAS 如果长时间不成功, 会给 CPU 带来非常大的执行开销","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是实现高并发下的计数, 可以使用 LongAdder, 设计的高并发思想真的强!","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2、著名的 \"ABA\" 问题","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CAS 需要在操作值的时候检查下值有没有发生变化, 如果没有发生变化则更新","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是如果一个值原来是A, 变成了B, 又变成了A, 那么使用 CAS 进行检查时会发现它的值没有发生变化, 但是实际上却变化了","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果感兴趣的小伙伴可以去看下 JUCA 原子包下的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AtomicStampedReference","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"ABA 问题背景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AtomicInteger 存在的一个问题, 也是大部分 Atomic 相关类存在的, 就是 ABA 问题","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"简短来说, 就是线程一获取到 AtomicInteger 的 value 为 0, 在准备做修改之前","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程二对 AtomicInteger 的 value 做了两次操作, 一次是将值修改为 1, 然后又将值修改为原来的 0","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此时线程一进行 CAS 操作, 发现内存中的值依旧是 0, OK, 更新成功, 结合下图了解","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ea/ea5f1986163f80f2acbfc9760c14765e.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"1、为什么线程二能够在线程一获取最新值后进行操作?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们以 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AtomicInteger#getAndIncrement","attrs":{}},{"type":"text","text":" 说明","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public final int getAndAddInt(Object var1, long var2, int var4) {\n int var5;\n do {\n \t// 标记1\n var5 = this.getIntVolatile(var1, var2);\n } while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));\n\n return var5;\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为我们的 CPU 执行是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"抢占式的, 时间片分配并不固定","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可能在线程一读取完标记1处之后, 时间片就分配执行了线程二, 此时线程一等待时间片的分配","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等线程二做完两步操作之后, 时间片分配到线程一, 这时才会继续执行","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2、ABA 会引发什么样的问题?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家应该都知道了 ABA 的行为是如何发生的, 列举网上的一个小例子, 大致意思如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、小明银行卡账户余额1万元, 去取款机取钱5千元, 正常应该发起一次请求, 因为网络或机器故障发起了两次请求","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各位大哥大姐不要擡杠哈, 不要说什么后端接口幂等, 取款机防重复提交之类的, ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"例子是为了说明问题","attrs":{}},{"type":"text","text":", 感谢 😄","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、线程一「由取款机发起」: 获取当前银行卡余额值1万元, 期望值5千元","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、线程二「由取款机发起」: 获取当前银行卡余额值1万元, 期望值5千元","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4、线程一成功了, 银行卡内余额剩余五千, 线程二未分配时间片, 获取到余额后被阻塞","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5、此时线程三「支付宝转入银行卡」: 获取当前银行卡余额5千元, 期望值1万元, 修改成功","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6、线程二获得时间片, 发现卡内余额是1万, 成功将卡内余额减少至5千","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"7、卡内余额原本应是1万-5千+5千=1万, 最终因为 ABA 问题导致 1万-5千+5千-5千=5千","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然正式业务中, 可能不会存在此类问题, 不过平常自己业务中使用到了原子类, 还是会埋下潜在隐患","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们先通过小程序代码来看下, ABA 问题是如何复现的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public static void main(String[] args) throws InterruptedException {\n AtomicInteger atomicInteger = new AtomicInteger(100);\n new Thread(() -> {\n atomicInteger.compareAndSet(100, 101);\n atomicInteger.compareAndSet(101, 100);\n }).start();\n\n Thread.sleep(1000);\n\n new Thread(() -> {\n boolean result = atomicInteger.compareAndSet(100, 101);\n System.out.println(String.format(\" >>> 修改 atomicInteger :: %s \", result));\n }).start();\n /**\n * >>> 修改 atomicInteger :: true \n */\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、创建一个初始值为100的 AtomicInteger, 线程一将100-> 101, 再从101->100","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、休眠1000ms, 防止时间片分配线程二提前执行","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、线程二从100->101, 修改成功","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"AtomicStampedReference","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先来说下解决 ABA 的思路吧, 也就是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AtomicStampedReference","attrs":{}},{"type":"text","text":" 的原理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内部维护了一个 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Pair 对象","attrs":{}},{"type":"text","text":", 存储了 value 值和一个版本号, 每次更新除了 value 值还会更新版本号","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static class Pair {\n \t// 存储值, 相当于上文的值100\n final T reference;\n \t// 类似于版本号的概念\n final int stamp;\n\n private Pair(T reference, int stamp) {\n this.reference = reference;\n this.stamp = stamp;\n }\n\t\t// 创建一个新的Pair对象, 每次值变化时都会创建一个新的对象\n static AtomicStampedReference.Pair of(T reference, int stamp) {\n return new AtomicStampedReference.Pair(reference, stamp);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们还是先通过一个小程序来了解下 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AtomicStampedReference","attrs":{}},{"type":"text","text":" 运行机制","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@SneakyThrows\npublic static void main(String[] args) {\n AtomicStampedReference stampedReference = new AtomicStampedReference(100, 0);\n\n new Thread(() -> {\n Thread.sleep(50);\n stampedReference.compareAndSet(100,\n 101,\n stampedReference.getStamp(),\n stampedReference.getStamp() + 1);\n\n stampedReference.compareAndSet(101,\n 100,\n stampedReference.getStamp(),\n stampedReference.getStamp() + 1);\n }).start();\n\n\n new Thread(() -> {\n int stamp = stampedReference.getStamp();\n Thread.sleep(500);\n boolean result = stampedReference.compareAndSet(100, 101, stamp, stamp + 1);\n System.out.println(String.format(\" >>> 修改 stampedReference :: %s \", result));\n }).start();\n \t/**\n \t\t\t* >>> 修改 atomicInteger :: false \n \t\t\t*/\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、创建 AtomicStampedReference, 并设置初始值100, 以及版本号0","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、线程一睡眠50ms, 然后对value做出操作100->101, 101->100, 版本+1","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为什么要睡眠50ms? 为了模拟多线程并发抢占, 让线程二先获取到版本号","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、线程二睡眠500ms, 等待线程一执行完毕, 开始将100->101, 版本号+1","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不出意外,\u0010 线程二一定会修改失败","attrs":{}},{"type":"text","text":", 虽然值相对应, 但是预期的版本号与 Pair 中的已不一致","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"compareAndSet","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"来看下它的 compareAndSet(...) 是如何做的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * 比较并设置\n *\n * @param expectedReference 预期值\n * @param newReference 期望值\n * @param expectedStamp 预期版本号\n * @param newStamp 期望版本号\n * @return\t\t\t\t\t\t\t\t\t是否成功\n */\npublic boolean compareAndSet(V expectedReference,\n V newReference,\n int expectedStamp,\n int newStamp) {\n // 获取当前 pair 引用\n AtomicStampedReference.Pair current = pair;\n // 预期值对比 pair value\n return expectedReference == current.reference &&\n \t\t\t// 预期版本号对比 pair stamp\n expectedStamp == current.stamp &&\n \t\t\t// 期望值对比 pair value\n ((newReference == current.reference &&\n \t\t\t// 期望版本号对比 pair stamp\n newStamp == current.stamp) ||\n \t\t\t\t// casPair\n casPair(current, Pair.of(newReference, newStamp)));\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果 compareAndSet(...) 为 True, 必须满足上述条件表达式中3个条件","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、预期值等于 pair value","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、预期版本号等于 pair stamp","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、期望值等于 pair value 并且期望版本号等于 pair stamp","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这是 value 和 版本号未发生变化时的场景","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4、当第一个条件和第二个条件满足, 但是不满足第三个条件, 证明值和版本号发生了变化, 创建 Pair 进行 CAS 比较替换","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"上述条件必须满足1、2、3或者满足1、2、4均可返回 True","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"将当前 Pair 原子引用切换为新 Pair, 与 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AtomicReference 思路一致","attrs":{}},{"type":"text","text":", 将对象引用进行原子转换","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private boolean casPair(AtomicStampedReference.Pair cmp, AtomicStampedReference.Pair val) {\n return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"使用 Integer value 的坑","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"小伙伴应该都知道, 如果你写出下面的代码, 是不会在堆内创建对象的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Integer i1 = 100;\nInteger i2 = 101;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为 JVM 会在常量池内存储两个对象, 而超过 -128~127 的数值则会在堆中创建对象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们 Pair 对象的 reference 是范型, 传递的 int 类型的数值会被转型, 变为 Integer","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用 Integer 类型并且值不在 -128~127 范围内, 会出现数据比对出错的情况, 大家看一下 compareAndSet(...) 就明白了","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"AtomicMarkableReference","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家看到上面的版本号两次操作必须保持不一致, 得自增或、自减或者别的操作, 相对而言也比较繁琐","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Doug Lea 大师同时也为我们提供了一个省事的类库 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AtomicMarkableReference","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"API 接口以及实现思路与上述的基本一致, 只不过把版本号 int 类型替换为了 boolean 类型, 其它无区别","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不过, ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AtomicMarkableReference 并不能完全解决 ABA 问题, 只是能够小概率预防","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"结言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于作者水平有限, 欢迎大家能够反馈指正文章中错误不正确的地方, 感谢 🙏","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"小伙伴的喜欢就是对我最大的支持, 如果读了文章有所收获, 希望能够 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"点赞、评论、关注三连!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"推荐阅读:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.cn/post/6891120931716988942","title":""},"content":[{"type":"text","text":"【强烈推荐】谨慎使用 JDK 8 新特性并行流 ParallelStream","attrs":{}}]}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.cn/post/6898869137724932110","title":""},"content":[{"type":"text","text":"【强烈推荐】一文快速掌握 Redisson 如何实现分布式锁原理","attrs":{}}]}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.cn/post/6896278031317663751","title":""},"content":[{"type":"text","text":"【强烈推荐】聊一聊 ReentrantLock 和 AQS 那点事","attrs":{}}]}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/sk5-MAtm-fmmOCMzTuznbg","title":""},"content":[{"type":"text","text":"【大厂面试真题】JDK 线程池中如何不超最大线程数快速消费任务","attrs":{}}]}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://juejin.cn/post/6897017564271280142","title":""},"content":[{"type":"text","text":"【大厂面试真题】JDK 线程池如何保证核心线程不被销毁","attrs":{}}]}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}},{"type":"strong","attrs":{}}],"text":"作者麻花,座标帝都 Java 后端研发,励志成为架构师的一枚处女座程序员,专注高并发、框架底层源码、分布式等知识分享","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章