java--JUC--CAS問題及解決方式
阿新 • • 發佈:2021-06-10
- CAS:
-
package com.model.CAS; import java.util.concurrent.atomic.AtomicInteger; /** * @Description:測試類 * @Author: 張紫韓 * @Crete 2021/6/10 13:47 */ public class CASDemo { /** * 什麼是CAS: compareAndSet 比較並交換 * 總結:比較當前工作記憶體的值和主記憶體的值,如果這個值是期望的,則執行操作,如果不是就一直迴圈(自旋鎖) * 缺點: * 1.迴圈耗時 * 2.一次性只能保證一個共享變數的原子性 * 3.會產生ABA問題 *
-
- CAS帶來的ABA問題:
-
package com.model.CAS; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicStampedReference; /** * @Description:測試類 * @Author: 張紫韓 * @Crete 2021/6/10 14:13 */ public class CAS_ABA { /** * 當一個執行緒去獲取一個值得時候 :獲取到之後 A=1,在執行 compareAndSet(1,2)之前 * 另一個執行緒也獲取到了A=1 ,然後快速的執行 compareAndSet(1,2),和compareAndSet(2,1) * 這是 第一個執行緒在執行 compareAndSet(1,2)時 他拿到的A=1已經不是最新的值,已經被其他的執行緒修改了 * */ public static void main(String[] args) { //解決ABA問題,使用原子引用, AtomicInteger atomicInteger = new AtomicInteger(2021); // AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(2020, 1); //搗亂的執行緒 new Thread(() ->{ System.out.println(Thread.currentThread().getName()+"獲取到了"+atomicInteger.get()); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022)); System.out.println(atomicInteger.get()); System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2022, 2021)); System.out.println(atomicInteger.get()); System.out.println(Thread.currentThread().getName()+"修改完畢"+atomicInteger.get()); },"B").start(); new Thread(() ->{ System.out.println(Thread.currentThread().getName()+"獲取到了"+atomicInteger.get()); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //被騙的執行緒 System.out.println(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022)); System.out.println(Thread.currentThread().getName()+"修改完畢"+atomicInteger.get()); },"A").start(); } }
- ABA問題解決,引用原子引用,
-
CAS:方法中會有比較,比較時的出現的坑 ,不能超出包裝類的 賦值範圍
-
package com.model.CAS; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicStampedReference; /** * @Description:測試類 * @Author: 張紫韓 * @Crete 2021/6/10 14:13 */ public class CAS_ABA_JJ { /** * 當一個執行緒去獲取一個值得時候 :獲取到之後 A=1,在執行 compareAndSet(1,2)之前 * 另一個執行緒也獲取到了A=1 ,然後快速的執行 compareAndSet(1,2),和compareAndSet(2,1) * 這是 第一個執行緒在執行 compareAndSet(1,2)時 他拿到的A=1已經不是最新的值,已經被其他的執行緒修改了 * */ public static void main(String[] args) { //解決ABA問題,使用原子引用, //大坑:AtomicStampedReference包裝類,注意,如果泛型是一個包裝類,注意物件的引用問題 // integer使用了物件快取機制,預設的範圍是-128 - 127,推薦使用靜態工廠模式的方法valueOf獲取物件,而不是new ,因為valueOf使用了快取, // 而new 一定會建立新的物件分配新的記憶體空間 AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1); //搗亂的執行緒 new Thread(() ->{ int stamp=atomicInteger.getStamp(); //獲得的版本號 System.out.println(Thread.currentThread().getName()+"獲得版本號"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(1, 2,atomicInteger.getStamp(),atomicInteger.getStamp()+1)); System.out.println(Thread.currentThread().getName()+"修改完畢版本號是:"+atomicInteger.getStamp()); System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2, 1,atomicInteger.getStamp(),atomicInteger.getStamp()+1)); System.out.println(Thread.currentThread().getName()+"修改完畢版本號是:"+atomicInteger.getStamp()); },"B").start(); new Thread(() ->{ System.out.println(Thread.currentThread().getName()+"獲取到版本號"+atomicInteger.getStamp()); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //被騙的執行緒 System.out.println(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022,atomicInteger.getStamp(),atomicInteger.getStamp()+1)); System.out.println(Thread.currentThread().getName()+"修改完畢"+atomicInteger.getStamp()); },"A").start(); } }
-