成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁技術文章
文章詳情頁

深入分析Java并發編程之CAS

瀏覽:7日期:2022-08-26 11:05:27

在Java并發編程的世界里,synchronized 和 Lock 是控制多線程并發環境下對共享資源同步訪問的兩大手段。其中 Lock 是 JDK 層面的鎖機制,是輕量級鎖,底層使用大量的自旋+CAS操作實現的。

學習并發推薦《Java并發編程的藝術》

那什么是CAS呢?CAS,compare and swap,即比較并交換,什么是比較并交換呢?在Lock鎖的理念中,采用的是一種樂觀鎖的形式,即多線程去修改共享資源時,不是在修改之前就加鎖,而是樂觀的認為沒有別的線程和自己爭鎖,就是通過CAS的理念去保障共享資源的安全性的。CAS的基本思想是,拿變量的原值和內存中的值進行比較,如果相同,則原值沒有被修改過,那么就將原值修改為新值,這兩步是原子的,能夠保證同一時間只有一個線程修改成功。這就是CAS的理念。

Java中要想使用CAS原子的修改某值,怎么做呢?幸運的是Java提供了這樣的API,就是在sun.misc.Unsafe.java類中。Unsafe,中文名不安全的,也被稱為魔術類,魔法類。

Unsafe類介紹

Unsafe類使Java擁有了像C語言的指針一樣操作內存空間的能力,一旦能夠直接操作內存,這也就意味著

(1)不受JVM管理,意思就是使用Unsafe操作內存無法被JVM GC,需要我們手動GC,稍有不慎就會出現內存泄漏。(2)Unsafe的不少方法中必須提供原始地址(內存地址)和被替換對象的地址,并且偏移量要自己計算(其提供的有計算偏移量的方法),所以一旦出現問題就是JVM崩潰級別的異常,會導致整個JVM實例崩潰,表現為應用程序直接crash掉。(3)直接操作內存,所以速度更快,在高并發的條件之下能夠很好地提高效率。

因此,從上面三個角度來看,雖然在一定程度上提升了效率但是也帶來了指針的不安全性。這也是它被取名為Unsafe的原因吧。

下面我們深入到源碼中看看,提供了什么方法直接操作內存。

打開Unsafe這個類,我們會發現里面有大量的被native關鍵字修飾的方法,這意味著這些方法是C語言提供的實現,底層調的是C語言的庫函數,我們無法直接看到他的源碼實現,需要去從OpenJDK去看了。另外還有一些基于native方法封裝的其他方法,整個Unsafe中的方法大致可以歸結為以下幾類:

(1)初始化操作(2)操作對象屬性(3)操作數組元素(4)線程掛起和恢復(5)CAS機制

CAS的使用

如果你學過java并發編程的話,稍微閱讀過JUC并發包里面的源碼的話,對這個Unsafe類一定不陌生,因為整個java并發包底層實現的核心就是靠它。JUC并發包中主要使用它提供的CAS(compare and swap,比較并交換)操作,原子的修改鎖的狀態和一些隊列元素。

沒看過JUC源碼的讀者也不用擔心,今天我們就是簡單介紹Unsafe類中的CAS操作,那么我們接下來就會通過一個簡單的例子來看看Unsafe的CAS是怎么使用的。

首先,使用這個類我們第一個要做的事情就是拿到這個類的實例,下面我們自定義了一個Util類用來獲取Unsafe的實例

import sun.misc.Unsafe;import java.lang.reflect.Field;public class UnsafeUtil { public static Unsafe reflectGetUnsafe() { try { Field field = Unsafe.class.getDeclaredField('theUnsafe'); field.setAccessible(true); return (Unsafe) field.get(null); } catch (Exception e) { e.printStackTrace(); } return null; }}

這個工具類通過反射的方式拿到Unsafe類中的一個名為theUnsafe字段,該字段是Unsafe類型,并在static塊中new一個Unsafe對象初始化這個字段(單例模式)。

然后我們定義了一個AtomicState類,這個類很簡單,有一個int型的state字段,還有一個Unsafe的常量,以及int型的offsetState,用來記錄state字段在AtomicState對象中的偏移量。具體代碼如下:

import com.walking.juc.util.UnsafeUtil;import sun.misc.Unsafe;public class AtomicState { private volatile int state = 0; public int getState() { return state; } private static final Unsafe UNSAFE = UnsafeUtil.reflectGetUnsafe(); private static final long offsetState; static { try { offsetState = UNSAFE.objectFieldOffset(AtomicState.class.getDeclaredField('state')); } catch (NoSuchFieldException e) { throw new Error(e); } } public final boolean compareAndSetState(int oldVal, int newVal) { return UNSAFE.compareAndSwapInt(this, offsetState, oldVal, newVal); }}

我們定義了一個compareAndSetState方法,需要傳兩個參數,分別是state的舊值和新值,也就是讀到的state的之前的值,以及想要把它修改成什么值,該方法內部調用的是Unsafe類的compareAndSwapInt方法,它有四個參數,分別是要修改的類實例對象、要修改的值的偏移量、舊值、新值。解釋一下偏移量,剛才我們提到Unsafe提供給我們直接訪問內存的能力,那么訪問內存肯定是要知道內存的地址在哪才能去修改其相應的值吧,我們看,第一個參數是對象實例引用,也就是說,已經知道這個對象的地址了,那么我們想修改這個對象里的state的值,就只需要計算出state在這個對象的偏移量就能找到state所在的內存地址,那就可以修改它了。

然后,我們通過一個測試類來驗證Unsafe的CAS操作。這個測試類我來解釋下大致的思想,我們弄5個線程,讓這個5個線程一個個啟動,我們無法保證線程同時開始啟動,那么我們有辦法保證這個5個線程同時執行我們的代碼,就是使用JUC包里的CyclicBarrier工具來實現的,這個工具初始化時需要傳入一個int值n,我們在線程的run方法內部在業務代碼執行之前調用CyclicBarrier的await方法,當指定數量n的線程都調用了這個方法那么這n個線程將同時往下執行,就像設置了一個屏障,所有人都達到這個屏障后,一起通過屏障,依次來模擬多線程并發

import lombok.extern.slf4j.Slf4j;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CountDownLatch;import java.util.concurrent.CyclicBarrier;@Slf4jpublic class TestAtomicState { static int tNum = 5;//線程數 我們開10個線程模擬多線程并發 static CyclicBarrier cyclicBarrier = new CyclicBarrier(tNum);//柵欄 static CountDownLatch countDownLatch = new CountDownLatch(tNum);//計數器 static AtomicState atomicState = new AtomicState(); public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= tNum; i++) { new Thread(new MyTask(),'t-'+i).start(); } countDownLatch.await();//為的是讓主線程在這句阻塞住,等待所有線程執行完畢(計數器減到0)再往下走 log.info('state最后的值:' + atomicState.getState()); } static class MyTask implements Runnable{ @Override public void run() { try { log.info(Thread.currentThread().getName() + '到達起跑線'); String name = Thread.currentThread().getName(); String substring = name.substring(name.indexOf('-') + 1); int i1 = Integer.parseInt(substring); cyclicBarrier.await();//設置一個屏障,所有線程達到這后開始一起往下執行 模擬并發 boolean b = atomicState.compareAndSetState(0, i1); if (b) { log.info('修改成功,tName:{}' ,Thread.currentThread().getName()); } else { log.info('修改失敗,tName:{}' ,Thread.currentThread().getName()); } } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } finally { countDownLatch.countDown();//線程執行完畢計數器減一 } } }}

在cyclicBarrier.await();之后我們調用AtomicState的compareAndSetState方法傳入舊值0和新值,新值就是線程名t-n中的n,哪個線程修改成功,最后state值就是線程名中的數字。至于CountDownLatch使用它的目的是讓mian線程等到t-1到t-5的線程全部執行完后打印state的值。我們的重點不是CyclicBarrier和CountDownLatch,知道它們是干什么的就行。

然后我們運行這個測試程序:

13:57:46.619 [t-2] INFO com.walking.castest.TestAtomicState - t-2到達起跑線13:57:46.619 [t-3] INFO com.walking.castest.TestAtomicState - t-3到達起跑線13:57:46.619 [t-5] INFO com.walking.castest.TestAtomicState - t-5到達起跑線13:57:46.619 [t-1] INFO com.walking.castest.TestAtomicState - t-1到達起跑線13:57:46.619 [t-4] INFO com.walking.castest.TestAtomicState - t-4到達起跑線13:57:46.628 [t-1] INFO com.walking.castest.TestAtomicState - 修改失敗,tName:t-113:57:46.628 [t-4] INFO com.walking.castest.TestAtomicState - 修改成功,tName:t-413:57:46.628 [t-2] INFO com.walking.castest.TestAtomicState - 修改失敗,tName:t-213:57:46.628 [t-5] INFO com.walking.castest.TestAtomicState - 修改失敗,tName:t-513:57:46.628 [t-3] INFO com.walking.castest.TestAtomicState - 修改失敗,tName:t-313:57:46.636 [main] INFO com.walking.castest.TestAtomicState - state最后的值:4

可以看到只有一個線程執行成功,這就是CAS的基本使用。

CAS的ABA問題

何為ABA問題呢?舉個例子,小明和小花合伙賣煎餅,不就后攢了10萬元,他們一起去銀行把錢存在他們公共的賬戶里,但是小明聽說最近牛市來了,就偷偷的把錢轉移到了股票市場,公共賬戶余額是0。1個月后股票賺了一筆錢,然后小明把之前轉移的10萬元又存到他們的公共賬戶。小明和小花一個月后又去存錢,去查賬戶余額是10萬。這就是ABA問題,簡單來說就是一個值本來是A,兩個線程同時都看到是A,然后線程1把A改成B后又改成A,線程1結束了。然后線程2去修改時,看到的是A,無法感知到這個過程中值發生過變化,對于線程2來說就發生了ABA的問題。

模擬ABA問題:

import java.util.concurrent.atomic.AtomicInteger;@Slf4jpublic class CAS_ABA_Stampe { static AtomicInteger atomicInteger = new AtomicInteger(10); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { try { log.info('{}拿到state的值為:{}', Thread.currentThread().getName(), atomicInteger.get()); log.info('{}第一次修改', Thread.currentThread().getName()); atomicInteger.getAndSet(0); Thread.sleep(2000); log.info('{}第二次修改', Thread.currentThread().getName()); atomicInteger.getAndSet(10); } catch (InterruptedException e) { e.printStackTrace(); } }, 't1'); t1.start(); Thread t2 = new Thread(() -> { try { log.info('{}第一次拿到state的值為:{}', Thread.currentThread().getName(), atomicInteger.get()); Thread.sleep(2500); log.info('{}第二次拿到state的值為:{}', Thread.currentThread().getName(), atomicInteger.get()); log.info('{}開始修改state的值為2', Thread.currentThread().getName()); atomicInteger.getAndSet(20); log.info('{}修改成功', Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } }, 't2'); t2.start(); t1.join(); t2.join(); log.info('最終state的值:{}', atomicInteger.get()); }}

//結果t2也能修改成功,并沒有發現這種變化15:12:35.999 [t1] INFO com.walking.castest.CAS_ABA_Stampe - t1拿到state的值為:1015:12:35.999 [t2] INFO com.walking.castest.CAS_ABA_Stampe - t2第一次拿到state的值為:1015:12:36.014 [t1] INFO com.walking.castest.CAS_ABA_Stampe - t1第一次修改15:12:38.015 [t1] INFO com.walking.castest.CAS_ABA_Stampe - t1第二次修改15:12:38.515 [t2] INFO com.walking.castest.CAS_ABA_Stampe - t2第二次拿到state的值為:1015:12:38.515 [t2] INFO com.walking.castest.CAS_ABA_Stampe - t2開始修改state的值為215:12:38.516 [t2] INFO com.walking.castest.CAS_ABA_Stampe - t2修改成功15:12:38.516 [main] INFO com.walking.castest.CAS_ABA_Stampe - 最終state的值:20

怎么解決CAS的ABA問題呢?

那就是基于版本號去解決,增加一個版本號的概念,每次被修改這個版本號就加1,版本號是一直向前的,版本號變了,就說明被修改過。

JUC包中提供了解決ABA問題的工具:

import lombok.extern.slf4j.Slf4j;import java.util.concurrent.atomic.AtomicStampedReference;@Slf4jpublic class CAS_ABA_Stampe { static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(10, 1); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { try { int stamp = stampedReference.getStamp(); int intValue = stampedReference.getReference().intValue(); log.info('{}私挪公款拿到stamp的值為:{},余額:{}', Thread.currentThread().getName(), stamp,intValue); stampedReference.compareAndSet(10, 0, stamp, stamp + 1); Thread.sleep(2000); stamp = stampedReference.getStamp(); intValue = stampedReference.getReference().intValue(); log.info('{}還回公款拿到stamp的值為:{},余額:{}', Thread.currentThread().getName(), stamp,intValue); stampedReference.compareAndSet(0, 10, stamp, stamp + 1); } catch (InterruptedException e) { e.printStackTrace(); } }, 't1'); t1.start(); Thread t2 = new Thread(() -> { try { int stamp = stampedReference.getStamp(); int intValue = stampedReference.getReference().intValue(); log.info('{}拿到stamp的值為:{},余額:{}', Thread.currentThread().getName(), stamp, intValue); Thread.sleep(3000); log.info('{}開始存款', Thread.currentThread().getName()); if (stampedReference.compareAndSet(10, 20, stamp, stamp + 1)) { log.info('{}款款成功', Thread.currentThread().getName()); }else { log.info('{}存款失敗,發現賬戶異常!!oldStamp:{},currentStamp:{}', Thread.currentThread().getName(),stamp,stampedReference.getStamp()); } } catch (InterruptedException e) { e.printStackTrace(); } }, 't2'); t2.start(); t1.join(); t2.join(); log.info('最終賬戶余額:{}W', stampedReference.getReference().intValue()); }}

運行結果:

15:32:37.488 [t1] INFO com.walking.castest.CAS_ABA_Stampe - t1私挪公款拿到stamp的值為:1,余額:1015:32:37.476 [t2] INFO com.walking.castest.CAS_ABA_Stampe - t2拿到stamp的值為:1,余額:1015:32:39.500 [t1] INFO com.walking.castest.CAS_ABA_Stampe - t1還回公款拿到stamp的值為:2,余額:015:32:40.498 [t2] INFO com.walking.castest.CAS_ABA_Stampe - t2開始存款15:32:40.498 [t2] INFO com.walking.castest.CAS_ABA_Stampe - t2存款失敗,發現賬戶異常!!oldStamp:1,currentStamp:315:32:40.498 [main] INFO com.walking.castest.CAS_ABA_Stampe - 最終賬戶余額:10W

t2存款時就發現賬戶異常,因為版本號已經變成了3,和t2剛開始拿到的不一樣,說明已經被別人修改過,從而解決ABA問題。

到這里CAS就完啦。別忘了點贊,轉發。

以上就是深入分析Java并發編程之CAS的詳細內容,更多關于Java并發編程之CAS的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久
国产精品久久久久久久久久妞妞| 夜夜嗨av一区二区三区中文字幕 | 国产成a人亚洲| 色婷婷av一区二区三区软件| 一区二区三区鲁丝不卡| 在线视频成人| 亚洲欧美色一区| 亚洲精品一区二| 国产精品久99| 欧美激情偷拍| 国产精品久久三| 欧美日本一区二区视频在线观看| 国产日韩欧美制服另类| 欧美成人一区二免费视频软件| 日韩精品一区二| 成人看片黄a免费看在线| 欧美浪妇xxxx高跟鞋交| 精品国产乱码久久| 精品一区二区国语对白| 久久三级福利| 男人的j进女人的j一区| 国产激情一区二区三区桃花岛亚洲| 在线观看视频一区| 久久99热狠狠色一区二区| 国产精品久久久久久久免费软件 | 亚洲大片一区二区三区| 欧美女人交a| 日韩一区二区三区视频在线| 五月天欧美精品| 欧美一区二区三区免费看| 欧美一区二区三区人| 久久99国产精品久久99| 一本色道久久综合亚洲精品按摩 | 亚洲精品写真福利| 精品9999| 中文字幕亚洲不卡| 国内一区二区在线视频观看| 国产情人综合久久777777| 91影院在线免费观看| 26uuu国产一区二区三区| 国产乱码精品一区二区三区五月婷 | 99国产精品国产精品毛片| 欧美另类一区二区三区| 日本不卡一区二区| 国产精品一级| 亚洲免费观看高清在线观看| 国内一区二区在线视频观看| 日本一区二区三区电影| 99久久精品情趣| 欧美一区二区观看视频| 亚洲香蕉伊在人在线观| 国产精品日韩二区| 亚洲成人三级小说| 欧美综合77777色婷婷| 亚洲成人你懂的| 亚洲欧美久久| 手机精品视频在线观看| 色呦呦日韩精品| 麻豆一区二区在线| 欧美另类z0zxhd电影| 国产99久久久国产精品潘金| 精品国产三级电影在线观看| 欧美二区在线| 亚洲日本一区二区| 美女久久一区| 久久疯狂做爰流白浆xx| 欧美电影在线免费观看| 成人黄色片在线观看| 久久亚洲精品小早川怜子| 99久久精品一区二区| 国产女同性恋一区二区| 欧美黄色一区| 亚洲欧美一区二区在线观看| 亚洲每日在线| 亚洲综合免费观看高清完整版| 在线亚洲一区二区| 国产美女在线观看一区| 日韩免费一区二区| 色综合天天综合网国产成人综合天 | 夜夜精品浪潮av一区二区三区| 在线亚洲伦理| 日本在线观看不卡视频| 欧美日韩午夜影院| 成人18视频日本| 国产精品久久久久久久久图文区 | 国产欧美午夜| 日本色综合中文字幕| 日本道色综合久久| 国产精品538一区二区在线| 日韩欧美一卡二卡| 欧美精品一卡| 国产精品全国免费观看高清| 国产精品毛片| 精品一区二区三区在线播放| 精品少妇一区二区三区在线播放| 极品少妇一区二区三区| 婷婷亚洲久悠悠色悠在线播放| 欧美亚洲国产一区二区三区| 午夜伊人狠狠久久| 欧美巨大另类极品videosbest| 欧美一区1区三区3区公司| 国产精品久久久久久久久免费相片| 国产美女一区| 国产高清视频一区| 综合久久久久久久| 欧美视频自拍偷拍| 欧美日本一区二区高清播放视频| 亚洲福利一二三区| 911精品国产一区二区在线| 91啪亚洲精品| 午夜影视日本亚洲欧洲精品| 日韩一级大片在线| 一区二区在线不卡| 日本不卡视频在线| 国产偷国产偷亚洲高清人白洁| 亚洲毛片视频| 日韩av一区二区在线影视| 制服丝袜亚洲精品中文字幕| 91亚洲精华国产精华精华液| 国产亚洲精品aa| 欧美专区日韩专区| 色综合天天综合网国产成人综合天 | 福利一区二区在线观看| 最好看的中文字幕久久| 日本高清不卡一区| 97精品超碰一区二区三区| 亚洲电影在线播放| 欧美大度的电影原声| 99久久国产免费看| 日韩精品一级二级| 国产肉丝袜一区二区| 在线观看亚洲精品| 国产精品v亚洲精品v日韩精品| 日本女优在线视频一区二区| 欧美成人性福生活免费看| 亚洲精品1234| 国产精品伊人色| 亚洲午夜日本在线观看| 久久久亚洲精品一区二区三区| 一本到一区二区三区| 91视频www| 喷水一区二区三区| 国产精品欧美综合在线| 欧美精品一二三区| 在线欧美日韩| 激情六月婷婷综合| 亚洲成年人网站在线观看| 久久综合色婷婷| 色8久久精品久久久久久蜜| 欧美精品九九| 国产成人自拍网| 亚洲www啪成人一区二区麻豆| 国产三级一区二区| 欧美另类videos死尸| 国产亚洲精品v| 99精品久久只有精品| 另类中文字幕网| 亚洲福利一区二区| 国产欧美日产一区| 51精品国自产在线| 国产一级久久| 盗摄精品av一区二区三区| 亚洲中国最大av网站| 精品电影一区二区| 色视频成人在线观看免| 激情综合网址| 91麻豆免费看片| 国产麻豆精品一区二区| 日韩精品久久久久久| 国产精品九色蝌蚪自拍| 久久一区二区视频| 欧美区在线观看| 久久久久成人精品免费播放动漫| 欧美日本不卡高清| 成人自拍视频在线| 精品在线播放午夜| 日韩国产欧美在线视频| 亚洲色图都市小说| 国产精品免费av| 蜜臀av一级做a爰片久久| 色综合久久中文字幕综合网| 日本乱人伦一区| 一本久道中文字幕精品亚洲嫩| 99免费精品视频| 亚洲二区视频在线| 亚洲一卡二卡三卡四卡无卡久久| 久久九九久久九九| 精品国产乱码久久久久久蜜臀 | 亚洲成人动漫av| 精品福利视频一区二区三区| 欧美精品一级二级三级| 欧美亚洲综合网| 国产亚洲在线观看| 亚洲伦理精品| 黄色精品一区| 狠狠色丁香久久综合频道| 欧美精品一区二区三区在线看午夜| 成人福利在线看| 成人深夜福利app| 丁香六月综合激情|