3.java并發(fā)工具

請帶著如下問題閱讀本文。
1.什么是原子操作?在Java Concurrency API中有哪些原子類(atomic classes)?
2.什么是AQS箍鼓?
3.什么是Callable和Future?
4.什么是FutureTask?
5.Semaphore有什么作用?
6.ReentrantReadWriteLock讀寫鎖的使用呵曹?
7.CyclicBarrier和CountDownLatch的用法及區(qū)別款咖?
8.LockSupport工具?
9.Condition接口及其實現(xiàn)原理奄喂?
10.Fork/Join框架的理解?

Atomic

原子操作是指一個不受其他操作影響的操作任務(wù)單元铐殃。原子操作是在多線程環(huán)境下避免數(shù)據(jù)不一致必須的手段。
java.util.concurrent.atomic包提供了int和long類型的裝類跨新,它們可以自動的保證對于他們的操作是原子的并且不需要使用同步富腊。   
AtomicBoolean,AtomicInteger域帐,AtomicLong赘被,AtomicReference

通過原子的方式更新數(shù)組里的某個元素,Atomic包提供了以3類
AtomicIntegerArray肖揣,AtomicLongArray,AtomicReferenceArray

如果需原子地更新某個類里的某個字段時民假,就需要使用原子更新字段類,Atomic包提供了以下3個類進行原子字段更新龙优。
AtomicLongFieldUpdater羊异,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater

public class AtomicIntegerFieldUpdaterTest { 
    // 創(chuàng)建原子更新器,并設(shè)置需要更新的對象類和對象的屬性
    private static AtomicIntegerFieldUpdater<User> a = 
            AtomicIntegerFieldUpdater.newUpdater(User.class, "old");
 
    public static void main(String[] args) throws InterruptedException {  
        // 設(shè)置柯南的年齡是10歲
        User conan = new User("conan", 10);
        // 柯南長了一歲彤断,但是仍然會輸出舊的年齡
        System.out.println(a.getAndIncrement(conan));
        // 輸出柯南現(xiàn)在的年齡
        System.out.println(a.get(conan));
    }
 
    public static class User {
        private String name;
        public volatile int old;
 
        public User(String name, int old) {
            this.name = name;
            this.old = old;
        }
 
        public String getName() {
            return name;
        }
 
        public int getOld() {
            return old;
        }
    }
}

AQS

為實現(xiàn)依賴于先進先出等待隊列的阻塞鎖和相關(guān)同步器提供一個框架野舶。
AQS使用一個FIFO的隊列表示排隊等待鎖的線程,隊列頭節(jié)點稱作“哨兵節(jié)點”或者“啞節(jié)點”宰衙,它不與任何線程關(guān)聯(lián)平道。其他的節(jié)點與等待線程關(guān)聯(lián),每個節(jié)點維護一個等待狀態(tài)waitStatus供炼。
AQS沒有鎖之類的概念一屋,它有個state變量句伶,是個int類型,在不同場合有著不同含義陆淀。
例如ReentrantLocky用它表示線程重入鎖的次數(shù),Semaphore用它表示剩余的許可數(shù)量先嬉,F(xiàn)utureTask用它表示任務(wù)的狀態(tài)轧苫。對state變量值的更新都采用CAS操作保證更新操作的原子性。

使用

為了將此類用作同步器的基礎(chǔ)疫蔓,需要適當(dāng)?shù)刂匦露x以下方法含懊,這是通過使用 getState()setState(int) 和/或 compareAndSetState(int, int) 方法來檢查和/或修改同步狀態(tài)來實現(xiàn)的:

默認情況下衅胀,每個方法都拋出 UnsupportedOperationException岔乔。這些方法的實現(xiàn)在內(nèi)部必須是線程安全的,通常應(yīng)該很短并且不被阻塞滚躯。定義這些方法是使用此類的 唯一受支持的方式雏门。其他所有方法都被聲明為 <tt style="color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">final</tt>,因為它們無法是各不相同的掸掏。

Callable,Future,FutureTask

Callable 和 Future 是比較有趣的一對組合茁影。當(dāng)我們需要獲取線程的執(zhí)行結(jié)果時,就需要用到它們丧凤。Callable用于產(chǎn)生結(jié)果募闲,F(xiàn)uture用于獲取結(jié)果。

Callable接口使用泛型去定義它的返回類型愿待。Executors類提供了一些有用的方法去在線程池中執(zhí)行Callable內(nèi)的任務(wù)浩螺。由于Callable任務(wù)是并行的,必須等待它返回的結(jié)果仍侥。java.util.concurrent.Future對象解決了這個問題要出。

在線程池提交Callable任務(wù)后返回了一個Future對象,使用它可以知道Callable任務(wù)的狀態(tài)和得到Callable返回的執(zhí)行結(jié)果农渊。Future提供了get()方法厨幻,等待Callable結(jié)束并獲取它的執(zhí)行結(jié)果。

代碼示例

Callable 是一個接口腿时,它只包含一個call()方法况脆。Callable是一個返回結(jié)果并且可能拋出異常的任務(wù)。

為了便于理解批糟,我們可以將Callable比作一個Runnable接口格了,而Callable的call()方法則類似于Runnable的run()方法。


image.png

什么是FutureTask?
FutureTask可用于異步獲取執(zhí)行結(jié)果或取消執(zhí)行任務(wù)的場景徽鼎。通過傳入Runnable或者Callable的任務(wù)給FutureTask盛末,直接調(diào)用其run方法或者放入線程池執(zhí)行弹惦,之后可以在外部通過FutureTask的get方法異步獲取執(zhí)行結(jié)果,因此悄但,F(xiàn)utureTask非常適合用于耗時的計算棠隐,主線程可以在完成自己的任務(wù)后,再去獲取結(jié)果檐嚣。另外助泽,F(xiàn)utureTask還可以確保即使調(diào)用了多次run方法,它都只會執(zhí)行一次Runnable或者Callable任務(wù)嚎京,或者通過cancel取消FutureTask的執(zhí)行等嗡贺。

常用并發(fā)工具

CountDownLatch

CyclicBarrier

Semaphore

Exchanger

CountDownLatch

countDownLatch允許一個或多個線程等待其他線程完成操作.比如說有三個線程分別是老二,老大,老爸,這三個線程必須是老二吃好了,老大吃,老大吃完了,老爸吃,在20年錢,農(nóng)村家里窮,一般有好吃的都是先留個最小的,然后才給其他兄弟姐妹吃,都不吃了,才由我們的父母吃,所以父母都不容易了,為了兒女,雖然是多個線程但是確實線性的,


image.png

這里要用JOIN。
如果是三個男的可以同時吃鞍帝,吃完之后诫睬,再由女的吃。就可以用COUNTDOWNLATCH

計時器必須大于等于0,只是等于0的時候,計時器就是0,調(diào)用await()方法時,不會阻塞當(dāng)前線程,CountDownLatch不可能重新初始化或者修改CountDownLatch對象的內(nèi)部 計數(shù)器的值,不可逆性.

Semaphore

semaphore(信號量)是用來控制同時訪問特定資源的線程數(shù)量,它通過協(xié)調(diào)各個線程,以保證合理的使用公共資源.這個跟隊列有點像,畫圖理解更直觀,


image.png

Exchane

Exchanger主要用于二個線程之間交換數(shù)據(jù),注意,只能是2個線程,如果有一個線程沒執(zhí)行exchange()方法,則會一直等待,線程就處于阻塞狀態(tài)了!如果怕一直等待,可以設(shè)置時間:exchange()有一個重載的方法.

class Man extends Thread {  
    Exchanger<Integer> exchanger = null;  
    public Man(Exchanger<Integer> exchanger) {  
        super();  
        this.exchanger = exchanger;  
    }  
    @Override  
    public void run() {  
        Random rand = new Random();
        int money = 0;
        for(int i=0;i<4;i++){
            money+=100000;//年薪在10萬以內(nèi)
            try {
                exchanger.exchange(money);//存錢
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }  
}  
/**
 * 女人
 */
class Girl extends Thread {  
    Exchanger<Integer> exchanger = null;  
    int money = 0;
    public Girl(Exchanger<Integer> exchanger) {  
        super();  
        this.exchanger = exchanger;  
    }  
    @Override  
    public void run() {  
        for(int i=0;i<4;i++){
            try {
                money = exchanger.exchange(money) ;
                System.out.println(money>300000?"親愛的"+money+"萬我們可以結(jié)婚了":money+"塊這么少,"+"臭屌絲活該單身,還不去賺錢娶老婆");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }  
} 

CyclicBarrier

CyclicBarrier帕涌,讓一組線程到達一個同步點后再一起繼續(xù)運行摄凡,在其中任意一個線程未達到同步點,其他到達的線程均會被阻塞蚓曼。

大家一致的意見都是CountDownLatch是計數(shù)器架谎,只能使用一次,而CyclicBarrier的計數(shù)器提供reset功能辟躏,可以多次使用谷扣。

對于CountDownLatch來說,重點是“一個線程(多個線程)等待”捎琐,而其他的N個線程在完成“某件事情”之后会涎,可以終止,也可以等待瑞凑。而對于CyclicBarrier末秃,重點是多個線程,在任意一個線程沒有完成籽御,所有的線程都必須等待练慕。

CountDownLatch是計數(shù)器,線程完成一個記錄一個技掏,只不過計數(shù)不是遞增而是遞減剪个,而CyclicBarrier更像是一個閥門坑资,需要所有線程都到達饵逐,閥門才能打開拙徽,然后繼續(xù)執(zhí)行。

Condition

在java.util.concurrent包中鸠真,有兩個很特殊的工具類悯仙,Condition和ReentrantLock龄毡,使用過的人都知道,ReentrantLock(重入鎖)是jdk的concurrent包提供的一種獨占鎖的實現(xiàn)

我們知道在線程的同步時可以使一個線程阻塞而等待一個信號锡垄,同時放棄鎖使其他線程可以能競爭到鎖

在synchronized中我們可以使用Object的wait()和notify方法實現(xiàn)這種等待和喚醒

但是在Lock中怎么實現(xiàn)這種wait和notify呢沦零?

答案是Condition,學(xué)習(xí)Condition主要是為了方便以后學(xué)習(xí)blockqueue和concurrenthashmap的源碼货岭,同時也進一步理解ReentrantLock路操。

正如WAIT 是需要用在SYNCHRONIZED 里面。CONDITION 也是需要和鎖配合來用

結(jié)合一段代碼來理解原理


image.png

Condition自己也維護了一個等待隊列茴她,該隊列的作用是維護一個等待signal信號的隊列,兩個隊列(AQS還有一個同步隊列)的作用是不同程奠,事實上丈牢,每個線程也僅僅會同時存在以上兩個隊列中的一個,流程是這樣的

線程1調(diào)用reentrantLock.lock時瞄沙,線程被加入到AQS的同步隊列中己沛。

線程1調(diào)用await方法被調(diào)用時,該線程從AQS同步隊列中移除距境,對應(yīng)操作是鎖的釋放申尼。

接著馬上被加入到Condition的等待隊列中,以為著該線程需要signal信號垫桂。

線程2师幕,因為線程1釋放鎖的關(guān)系,被喚醒诬滩,并判斷可以獲取鎖霹粥,于是線程2獲取鎖,并被加入到AQS的同步隊列中疼鸟。

線程2調(diào)用signal方法后控,這個時候Condition的等待隊列中只有線程1一個節(jié)點,于是它被取出來空镜,并被加入到AQS的同步隊列中浩淘。 注意,這個時候吴攒,線程1 并沒有被喚醒张抄。

signal方法執(zhí)行完畢,線程2調(diào)用reentrantLock.unLock()方法洼怔,釋放鎖欣鳖。這個時候因為AQS中只有線程1,于是茴厉,AQS釋放鎖后按從頭到尾的順序喚醒線程時泽台,線程1被喚醒什荣,于是線程1回復(fù)執(zhí)行。

直到釋放所整個過程執(zhí)行完畢怀酷。

可以看到稻爬,整個協(xié)作過程是靠結(jié)點在AQS的同步隊列和Condition的等待隊列中來回移動實現(xiàn)的,Condition作為一個條件類蜕依,很好的自己維護了一個等待信號的隊列桅锄,并在適時的時候?qū)⒔Y(jié)點加入到AQS的同步隊列中來實現(xiàn)的喚醒操作。

LockSupport

LockSupport是JDK中比較底層的類样眠,用來創(chuàng)建鎖和其他同步工具類的基本線程阻塞友瘤。java鎖和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是通過調(diào)用 LockSupport .park()和 LockSupport .unpark()實現(xiàn)線程的阻塞和喚醒 的檐束。

LockSupport 很類似于二元信號量(只有1個許可證可供使用)辫秧,如果這個許可還沒有被占用,當(dāng)前線程獲取許可并繼 續(xù) 執(zhí)行被丧;如果許可已經(jīng)被占用盟戏,當(dāng)前線 程阻塞,等待獲取許可甥桂。

相比較SUSPEND柿究,他不會引起線程凍結(jié)。假設(shè)一個線程先UNPARK了你黄选,你再PARK蝇摸,你不會被掛起。

底層是調(diào)用UNSAFE.UNPARK來做办陷。

Fork/Join 框架理解

Fork/Join是什么
Oracle的官方給出的定義是:Fork/Join框架是一個實現(xiàn)了ExecutorService接口的多線程處理器探入。它可以把一個大的任務(wù)劃分為若干個小的任務(wù)并發(fā)執(zhí)行,充分利用可用的資源懂诗,進而提高應(yīng)用的執(zhí)行效率蜂嗽。

我們再通過Fork和Join這兩個單詞來理解下Fork/Join框架,F(xiàn)ork就是把一個大任務(wù)切分為若干子任務(wù)并行的執(zhí)行殃恒,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果植旧,最后得到這個大任務(wù)的結(jié)果。

比如計算1+2+离唐。病附。+10000,可以分割成10個子任務(wù)亥鬓,每個子任務(wù)分別對1000個數(shù)進行求和完沪,最終匯總這10個子任務(wù)的結(jié)果。

工作竊取算法是指線程從其他任務(wù)隊列中竊取任務(wù)執(zhí)行(可能你會很詫異,這個算法有什么用覆积。待會你就知道了)听皿。考慮下面這種場景:有一個很大的計算任務(wù)宽档,為了減少線程的競爭尉姨,會將這些大任務(wù)切分為小任務(wù)并分在不同的隊列等待執(zhí)行,然后為每個任務(wù)隊列創(chuàng)建一個線程執(zhí)行隊列的任務(wù)吗冤。那么問題來了又厉,有的線程可能很快就執(zhí)行完了,而其他線程還有任務(wù)沒執(zhí)行完椎瘟,執(zhí)行完的線程與其空閑下來不如幫助其他線程執(zhí)行任務(wù)覆致,這樣也能加快執(zhí)行進程。所以肺蔚,執(zhí)行完的空閑線程從其他隊列的尾部竊取任務(wù)執(zhí)行煌妈,而被竊取任務(wù)的線程則從隊列的頭部取任務(wù)執(zhí)行(這里使用了雙端隊列,既不影響被竊取任務(wù)的執(zhí)行過程又能加快執(zhí)行進度)婆排。

Fork/Join實現(xiàn)了ExecutorService声旺,所以它的任務(wù)也需要放在線程池中執(zhí)行笔链。它的不同在于它使用了工作竊取算法段只,空閑的線程可以從滿負荷的線程中竊取任務(wù)來幫忙執(zhí)行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鉴扫,一起剝皮案震驚了整個濱河市赞枕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌坪创,老刑警劉巖炕婶,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異莱预,居然都是意外死亡柠掂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門依沮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涯贞,“玉大人,你說我怎么就攤上這事危喉∷斡妫” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵辜限,是天一觀的道長皇拣。 經(jīng)常有香客問我,道長薄嫡,這世上最難降的妖魔是什么氧急? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任颗胡,我火速辦了婚禮,結(jié)果婚禮上态蒂,老公的妹妹穿的比我還像新娘杭措。我一直安慰自己,他們只是感情好钾恢,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布手素。 她就那樣靜靜地躺著,像睡著了一般瘩蚪。 火紅的嫁衣襯著肌膚如雪泉懦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天疹瘦,我揣著相機與錄音崩哩,去河邊找鬼。 笑死言沐,一個胖子當(dāng)著我的面吹牛邓嘹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播险胰,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼汹押,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了起便?” 一聲冷哼從身側(cè)響起棚贾,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榆综,沒想到半個月后妙痹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡鼻疮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年怯伊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片判沟。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡耿芹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出水评,到底是詐尸還是另有隱情猩系,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布中燥,位于F島的核電站寇甸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拿霉,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一吟秩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绽淘,春花似錦涵防、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至杀怠,卻和暖如春椰憋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赔退。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工橙依, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人硕旗。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓窗骑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漆枚。 傳聞我的和親對象是個殘疾皇子创译,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容