一.進(jìn)程和線程區(qū)別
Java線程的5種狀態(tài)及切換(透徹講解) - CSDN博客
二.輕量級鎖與偏向鎖
三.CAS
在JDK 5之前Java語言是靠synchronized關(guān)鍵字保證同步的衬衬,這會(huì)導(dǎo)致有鎖
鎖機(jī)制存在以下問題:
(1)在多線程競爭下,加鎖毙替、釋放鎖會(huì)導(dǎo)致比較多的上下文切換和調(diào)度延時(shí),引起性能問題价捧。
(2)一個(gè)線程持有鎖會(huì)導(dǎo)致其它所有需要此鎖的線程掛起次坡。
(3)如果一個(gè)優(yōu)先級高的線程等待一個(gè)優(yōu)先級低的線程釋放鎖會(huì)導(dǎo)致優(yōu)先級倒置,引起性能風(fēng)險(xiǎn)阱飘。
volatile是不錯(cuò)的機(jī)制怔锌,但是volatile不能保證原子性炕置。因此對于同步最終還是要回到鎖機(jī)制上來诚些。
獨(dú)占鎖是一種悲觀鎖,synchronized就是一種獨(dú)占鎖氢哮,會(huì)導(dǎo)致其它所有需要鎖的線程掛起袋毙,等待持有鎖的線程釋放鎖。而另一個(gè)更加有效的鎖就是樂觀鎖冗尤。所謂樂觀鎖就是听盖,每次不加鎖而是假設(shè)沒有沖突而去完成某項(xiàng)操作,如果因?yàn)闆_突失敗就重試裂七,直到成功為止皆看。樂觀鎖用到的機(jī)制就是CAS,Compare and Swap背零。
二悬蔽、CAS的目的
利用CPU的CAS指令,同時(shí)借助JNI來完成Java的非阻塞算法捉兴。其它原子操作都是利用類似的特性完成的。而整個(gè)J.U.C都是建立在CAS之上的录语,因此對于synchronized阻塞算法倍啥,J.U.C在性能上有了很大的提升。
三澎埠、CAS存在的問題
CAS雖然很高效的解決原子操作虽缕,但是CAS仍然存在三大問題。ABA問題蒲稳,循環(huán)時(shí)間長開銷大和只能保證一個(gè)共享變量的原子操作
1.ABA問題氮趋。因?yàn)镃AS需要在操作值的時(shí)候檢查下值有沒有發(fā)生變化,如果沒有發(fā)生變化則更新江耀,但是如果一個(gè)值原來是A剩胁,變成了B,又變成了A祥国,那么使用CAS進(jìn)行檢查時(shí)會(huì)發(fā)現(xiàn)它的值沒有發(fā)生變化昵观,但是實(shí)際上卻變化了。ABA問題的解決思路就是使用版本號舌稀。在變量前面追加上版本號啊犬,每次變量更新的時(shí)候把版本號加一,那么A-B-A 就會(huì)變成1A-2B-3A壁查。
從Java1.5開始JDK的atomic包里提供了一個(gè)類AtomicStampedReference來解決ABA問題觉至。這個(gè)類的compareAndSet方法作用是首先檢查當(dāng)前引用是否等于預(yù)期引用,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志睡腿,如果全部相等语御,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值峻贮。
關(guān)于ABA問題參考文檔:?http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html
2. 循環(huán)時(shí)間長開銷大。自旋CAS如果長時(shí)間不成功沃暗,會(huì)給CPU帶來非常大的執(zhí)行開銷月洛。如果JVM能支持處理器提供的pause指令那么效率會(huì)有一定的提升,pause指令有兩個(gè)作用孽锥,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會(huì)消耗過多的執(zhí)行資源嚼黔,延遲的時(shí)間取決于具體實(shí)現(xiàn)的版本,在一些處理器上延遲時(shí)間是零惜辑。第二它可以避免在退出循環(huán)的時(shí)候因內(nèi)存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush)唬涧,從而提高CPU的執(zhí)行效率。
3. 只能保證一個(gè)共享變量的原子操作盛撑。當(dāng)對一個(gè)共享變量執(zhí)行操作時(shí)碎节,我們可以使用循環(huán)CAS的方式來保證原子操作,但是對多個(gè)共享變量操作時(shí)抵卫,循環(huán)CAS就無法保證操作的原子性狮荔,這個(gè)時(shí)候就可以用鎖,或者有一個(gè)取巧的辦法介粘,就是把多個(gè)共享變量合并成一個(gè)共享變量來操作殖氏。比如有兩個(gè)共享變量i=2,j=a,合并一下ij=2a姻采,然后用CAS來操作ij雅采。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個(gè)變量放在一個(gè)對象里來進(jìn)行CAS操作慨亲。
四婚瓜、concurrent包的實(shí)現(xiàn)
由于java的CAS同時(shí)具有 volatile 讀和volatile寫的內(nèi)存語義,因此Java線程之間的通信現(xiàn)在有了下面四種方式:
A線程寫volatile變量刑棵,隨后B線程讀這個(gè)volatile變量巴刻。
A線程寫volatile變量,隨后B線程用CAS更新這個(gè)volatile變量蛉签。
A線程用CAS更新一個(gè)volatile變量冈涧,隨后B線程用CAS更新這個(gè)volatile變量。
A線程用CAS更新一個(gè)volatile變量正蛙,隨后B線程讀這個(gè)volatile變量督弓。
Java的CAS會(huì)使用現(xiàn)代處理器上提供的高效機(jī)器級別原子指令,這些原子指令以原子方式對內(nèi)存執(zhí)行讀-改-寫操作乒验,這是在多處理器中實(shí)現(xiàn)同步的關(guān)鍵(從本質(zhì)上來說愚隧,能夠支持原子性讀-改-寫指令的計(jì)算機(jī)器,是順序計(jì)算圖靈機(jī)的異步等價(jià)機(jī)器,因此任何現(xiàn)代的多處理器都會(huì)去支持某種能對內(nèi)存執(zhí)行原子性讀-改-寫操作的原子指令)狂塘。同時(shí)录煤,volatile變量的讀/寫和CAS可以實(shí)現(xiàn)線程之間的通信。把這些特性整合在一起荞胡,就形成了整個(gè)concurrent包得以實(shí)現(xiàn)的基石妈踊。如果我們仔細(xì)分析concurrent包的源代碼實(shí)現(xiàn),會(huì)發(fā)現(xiàn)一個(gè)通用化的實(shí)現(xiàn)模式:
首先泪漂,聲明共享變量為volatile廊营;
然后,使用CAS的原子條件更新來實(shí)現(xiàn)線程之間的同步萝勤;
同時(shí)露筒,配合以volatile的讀/寫和CAS所具有的volatile讀和寫的內(nèi)存語義來實(shí)現(xiàn)線程之間的通信。
AQS敌卓,非阻塞數(shù)據(jù)結(jié)構(gòu)和原子變量類(java.util.concurrent.atomic包中的類)慎式,這些concurrent包中的基礎(chǔ)類都是使用這種模式來實(shí)現(xiàn)的,而concurrent包中的高層類又是依賴于這些基礎(chǔ)類來實(shí)現(xiàn)的趟径。從整體來看瘪吏,concurrent包的實(shí)現(xiàn)示意圖如下:
四.CAS自旋
五.自旋
六.java并發(fā)編程中CountDownLatch和CyclicBarrier的使用?
在多線程程序設(shè)計(jì)中,經(jīng)常會(huì)遇到一個(gè)線程等待一個(gè)或多個(gè)線程的場景蜗巧,遇到這樣的場景應(yīng)該如何解決肪虎?
如果是一個(gè)線程等待一個(gè)線程,則可以通過await()和notify()來實(shí)現(xiàn)惧蛹;
如果是一個(gè)線程等待多個(gè)線程,則就可以使用CountDownLatch和CyclicBarrier來實(shí)現(xiàn)比較好的控制刑枝。
下面來詳細(xì)描述下CountDownLatch的應(yīng)用場景:
例如:百米賽跑:8名運(yùn)動(dòng)員同時(shí)起跑香嗓,由于速度的快慢,肯定有會(huì)出現(xiàn)先到終點(diǎn)和晚到終點(diǎn)的情況装畅,而終點(diǎn)有個(gè)統(tǒng)計(jì)成績的儀器靠娱,當(dāng)所有選手到達(dá)終點(diǎn)時(shí),它會(huì)統(tǒng)計(jì)所有人的成績并進(jìn)行排序掠兄,然后把結(jié)果發(fā)送到匯報(bào)成績的系統(tǒng)像云。
其實(shí)這就是一個(gè)CountDownLatch的應(yīng)用場景:一個(gè)線程或多個(gè)線程等待其他線程運(yùn)行達(dá)到某一目標(biāo)后進(jìn)行自己的下一步工作,而被等待的“其他線程”達(dá)到這個(gè)目標(biāo)后繼續(xù)自己下面的任務(wù)蚂夕。
這個(gè)場景中:
1. 被等待的“其他線程”------>8名運(yùn)動(dòng)員
2. 等待“其他線程”的這個(gè)線程------>終點(diǎn)統(tǒng)計(jì)成績的儀器
那么迅诬,如何來通過CountDownLatch來實(shí)現(xiàn)上述場景的線程控制和調(diào)度呢?
jdk中CountDownLatch類有一個(gè)常用的構(gòu)造方法:CountDownLatch(int count)婿牍;
????????????????????????兩個(gè)常用的方法:await()和countdown()?
其 中count是一個(gè)計(jì)數(shù)器中的初始化數(shù)字侈贷,比如初始化的數(shù)字是2,當(dāng)一個(gè)線程里調(diào)用了countdown()等脂,則這個(gè)計(jì)數(shù)器就減一俏蛮,當(dāng)線程調(diào)用了 await()撑蚌,則這個(gè)線程就等待這個(gè)計(jì)數(shù)器變?yōu)?,當(dāng)這個(gè)計(jì)數(shù)器變?yōu)?時(shí)搏屑,這個(gè)線程繼續(xù)自己下面的工作争涌。下面是上述CountDownLatch場景的 實(shí)現(xiàn):
Work類(運(yùn)動(dòng)員):
import?Java.util.concurrent.CountDownLatch;
public class Work implements Runnable {
?private int id;
?private CountDownLatch beginSignal;
?private CountDownLatch endSignal;
?public Work(int id, CountDownLatch begin, CountDownLatch end) {
??this.id = id;
??this.beginSignal = begin;
??this.endSignal = end;
?}
?@Override
?public void run() {
??try {
???beginSignal.await();
???System.out.println("起跑...");
???System.out.println("work" + id + "到達(dá)終點(diǎn)");
???endSignal.countDown();
???System.out.println("work" + id + "繼續(xù)干其他事情");
??} catch (InterruptedException e) {
???// TODO Auto-generated catch block
???e.printStackTrace();
??}
?}
}
Main類(終點(diǎn)統(tǒng)計(jì)儀器):
import java.util.concurrent.CountDownLatch;
public class Main {
?public static void main(String[] args) {
??CountDownLatch begSignal = new CountDownLatch(1);
??CountDownLatch endSignal = new CountDownLatch(8);
??for (int i = 0; i < 8; i++) {
???new Thread(new Work(i, begSignal, endSignal)).start();
??}
??try {
???begSignal.countDown();? //統(tǒng)一起跑
???endSignal.await();????? //等待運(yùn)動(dòng)員到達(dá)終點(diǎn)
???System.out.println("結(jié)果發(fā)送到匯報(bào)成績的系統(tǒng)");
??} catch (InterruptedException e) {
???e.printStackTrace();
??}
?}
}
下面詳細(xì)描述下CyclicBarrier的應(yīng)用場景:
有四個(gè)游戲玩家玩游戲,游戲有三個(gè)關(guān)卡辣恋,每個(gè)關(guān)卡必須要所有玩家都到達(dá)后才能允許通關(guān)亮垫。
其 實(shí)這個(gè)場景里的玩家中如果有玩家A先到了關(guān)卡1,他必須等待其他所有玩家都到達(dá)關(guān)卡1時(shí)才能通過抑党,也就是說線程之間需要互相等待包警,這和 CountDownLatch的應(yīng)用場景有區(qū)別,CountDownLatch里的線程是到了運(yùn)行的目標(biāo)后繼續(xù)干自己的其他事情底靠,而這里的線程需要等待其 他線程后才能繼續(xù)完成下面的工作害晦。
jdk中CyclicBarrier類有兩個(gè)常用的構(gòu)造方法:
1. CyclicBarrier(int parties)
這里的parties也是一個(gè)計(jì)數(shù)器,例如暑中,初始化時(shí)parties里的計(jì)數(shù)是3壹瘟,于是擁有該CyclicBarrier對象的線程當(dāng)parties的計(jì)數(shù)為3時(shí)就喚醒,注:這里parties里的計(jì)數(shù)在運(yùn)行時(shí)當(dāng)調(diào)用CyclicBarrier:await()時(shí),計(jì)數(shù)就加1鳄逾,一直加到初始的值
2. CyclicBarrier(int parties, Runnable barrierAction)
這里的parties與上一個(gè)構(gòu)造方法的解釋是一樣的稻轨,這里需要解釋的是第二個(gè)入?yún)ⅲ≧unnable barrierAction),這個(gè)參數(shù)是一個(gè)實(shí)現(xiàn)Runnable接口的類的對象,也就是說當(dāng)parties加到初始值時(shí)就出發(fā)barrierAction的內(nèi)容雕凹。
下面來實(shí)現(xiàn)上述的應(yīng)用場景:
?Player類(玩家類)
[java]?view plain?copy
import?java.util.concurrent.BrokenBarrierException;??
import?java.util.concurrent.CyclicBarrier;??
public?class?Player?implements?Runnable?{??
?private?CyclicBarrier?cyclicBarrier;??
?private?int?id;??
?public?Player(int?id,?CyclicBarrier?cyclicBarrier)?{??
??this.cyclicBarrier?=?cyclicBarrier;??
??this.id?=?id;??
?}??
?@Override??
?public?void?run()?{??
??try?{??
???System.out.println("玩家"?+?id?+?"正在玩第一關(guān)...");??
???cyclicBarrier.await();??
???System.out.println("玩家"?+?id?+?"進(jìn)入第二關(guān)...");??
??}?catch?(InterruptedException?e)?{??
???e.printStackTrace();??
??}?catch?(BrokenBarrierException?e)?{??
???e.printStackTrace();??
??}??
?}??
}??
GameBarrier類(關(guān)卡類殴俱,這里控制玩家必須全部到達(dá)第一關(guān)結(jié)束的關(guān)口才能進(jìn)入第二關(guān))
import java.util.concurrent.CyclicBarrier;
public class GameBarrier {
?public static void main(String[] args) {
??CyclicBarrier cyclicBarrier = new CyclicBarrier(4, new Runnable() {
???@Override
???public void run() {
????System.out.println("所有玩家進(jìn)入第二關(guān)!");
???}
??});
??for (int i = 0; i < 4; i++) {
???new Thread(new Player(i, cyclicBarrier)).start();
??}
?}
}
七.線程池
Java并發(fā)編程:線程池的使用 - 海 子 - 博客園
八.ThreadLocal
Java并發(fā)編程:深入剖析ThreadLocal - 海 子 - 博客園
九.50到j(luò)ava多線程面試題
Java線程面試題 Top 50 (轉(zhuǎn)載) - 海 子 - 博客園
JAVA多線程和并發(fā)基礎(chǔ)面試問答(轉(zhuǎn)載) - 海 子 - 博客園