多线程技术点二
1.Java線程之間的通信由Java內(nèi)存模型控制(JMM)。
JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲(chǔ)在主內(nèi)存中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存中存儲(chǔ)了該線程以讀/寫共享變量的副本。
2.先行發(fā)生原則(happens-before)
3.數(shù)據(jù)依賴性
如果兩個(gè)操作訪問同一個(gè)變量,且這兩個(gè)操作中有一個(gè)為寫操作,此時(shí)這兩個(gè)操作之間就存在數(shù)據(jù)依賴性。
數(shù)據(jù)依賴分為3種類型:寫后讀,寫后寫,讀后寫
編譯器和處理器在重排序時(shí),會(huì)遵守?cái)?shù)據(jù)依賴性,編譯器和處理器不會(huì)改變存在數(shù)據(jù)依賴關(guān)系的兩個(gè)操作的執(zhí)行順序。
4.as-if-serial語義
不管怎么重排序,(單線程)程序的執(zhí)行結(jié)果不能被改變。
5.JMM和順序一致性模型的差異
1)順序一致性模型保證單線程內(nèi)的操作會(huì)按照程序的順序執(zhí)行,而JMM模型不保證單線程內(nèi)的操作會(huì)按照程序的順序執(zhí)行(臨界區(qū)內(nèi)的重排序)
2)順序一致性模型保證所有線程只能看到一致的操作執(zhí)行順序,而JMM不保證所有線程能看到一致的操作執(zhí)行順序
3)JMM不保證對(duì)64位的long型和double型變量的寫操作具有原子性。
6.volatile關(guān)鍵字
可以把對(duì)volatile變量的單個(gè)讀/寫看成是使用同一個(gè)鎖對(duì)這些單個(gè)讀/寫操作做了同步。
volatile變量自身具有下列特性
1)可見性:對(duì)一個(gè)volatile變量的讀,總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫入
2)原子性:對(duì)任意單個(gè)volatile變量的讀/寫具有原子性,但類似于volatile++這種復(fù)合操作不具有原子性
7.volatile寫-讀的內(nèi)存語義
1)當(dāng)寫一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存
2)當(dāng)讀一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無效。線程接下來將從主內(nèi)存中讀取共享變量
8.重排序分為編譯器重排序和處理器重排序,為了實(shí)現(xiàn)volatile內(nèi)存語義,JMM會(huì)分別限制這兩種類型重排序類型。為了實(shí)現(xiàn)volatile的內(nèi)存語義,編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來禁止特定類型的處理器重排序。
9.鎖的釋放和獲取的內(nèi)存語義
當(dāng)線程釋放鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存中。
當(dāng)線程獲取鎖時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無效。從而使得被監(jiān)視器保護(hù)的臨界區(qū)代碼必須從主內(nèi)存中讀取共享變量。
10.雙重檢查鎖定的問題根源
instance = new Singleton()這行代碼可以分解為如下3行偽代碼
memory=allocate();//分配對(duì)象的內(nèi)存空間
ctorInstance(memory);//初始化對(duì)象
instance=memory;//設(shè)置instance指向剛分配的內(nèi)存地址
解決方案:將instance聲明為volatile型,就可以實(shí)現(xiàn)線程安全的延遲初始化。
11.隊(duì)列同步器
用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架,它使用了一個(gè)int成員變量表示同步狀態(tài),通過內(nèi)置的FIFO隊(duì)列來完成資源獲取線程的排隊(duì)工作。
同步器的主要使用方式是繼承,子類通過繼承同步器并實(shí)現(xiàn)它的抽象方法來管理同步狀態(tài),在抽象方法的實(shí)現(xiàn)過程中通過同步器提供的3個(gè)方法對(duì)同步狀態(tài)進(jìn)行修改(getState()、setState(int newState)和compareAndSetState(int expect, int update))。
子類推薦被定義為自定義同步組件的靜態(tài)內(nèi)部類。
12.同步器的設(shè)計(jì)是基于模板方法模式的,即使用者需要繼承同步器并重寫指定的方法,隨后將同步器組合在自定義同組組件的實(shí)現(xiàn)中,并調(diào)用同步器提供的模板方法。
13.同步器可重寫的方法:
protected boolean tryAcquire(int arg)——獨(dú)占式獲取同步狀態(tài),實(shí)現(xiàn)該方法需要查詢當(dāng)前狀態(tài)并判斷同步狀態(tài)是否復(fù)合預(yù)期,然后再進(jìn)行CAS設(shè)置同步狀態(tài)。
protected boolean tryRelease(int arg)——獨(dú)占式釋放同步狀態(tài),等待獲取同步狀態(tài)的線程將有機(jī)會(huì)獲取同步狀態(tài)。
protected int tryAcquireShared(int arg)——共享式獲取同步狀態(tài),返回大于等于0的值,表示獲取成功,反之,獲取失敗。
protected boolean tryReleaseShared(int arg)——共享式釋放同步狀態(tài)
protected boolean isHeldExecusively()——當(dāng)前同步器是否在獨(dú)占模式下被線程占用,一般該方法表示是否被當(dāng)前線程所獨(dú)占。
14.隊(duì)列同步器的實(shí)現(xiàn)分析
1)同步隊(duì)列——
?同步隊(duì)列中的節(jié)點(diǎn)(Node)用來保存獲取同步狀態(tài)失敗的線程引用、等待狀態(tài)以及前驅(qū)和后繼節(jié)點(diǎn)。
同步器包含了兩個(gè)節(jié)點(diǎn)類型的引用,一個(gè)指向頭節(jié)點(diǎn),而另一個(gè)指向尾節(jié)點(diǎn)。為了保證未能成功獲取同步狀態(tài)的線程加入同步隊(duì)列過程的線程安全性,同步器提供了一個(gè)基于CAS的設(shè)置尾節(jié)點(diǎn)的方法:compareAndSetTail(Node expect, Node update),它需要傳遞當(dāng)前線程“認(rèn)為”的尾節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn),只有設(shè)置成功后,當(dāng)前節(jié)點(diǎn)才正式與之前的尾節(jié)點(diǎn)建立關(guān)聯(lián)。
節(jié)點(diǎn)進(jìn)入同步隊(duì)列之后,就進(jìn)入了一個(gè)自旋的過程,每個(gè)節(jié)點(diǎn)都在自省地觀察,當(dāng)滿足條件,獲取到了同步狀態(tài),就可以從這個(gè)自旋過程中退出。
2)獨(dú)占式同步狀態(tài)獲取與釋放
在獲取同步狀態(tài)時(shí),同步器維護(hù)一個(gè)同步隊(duì)列,獲取狀態(tài)失敗的線程都會(huì)被加入到隊(duì)列中并在隊(duì)列中進(jìn)行自旋;移出隊(duì)列(或停止自旋)的條件是前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)且成功獲取了同步狀態(tài)。在釋放同步狀態(tài)時(shí),同步器調(diào)用tryRelease(int arg)方法釋放同步狀態(tài),然后喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)。
3)共享式同步狀態(tài)獲取與釋放
共享式和獨(dú)占式獲取最主要的區(qū)別在于同一時(shí)刻能否有多個(gè)線程同時(shí)獲取到同步狀態(tài)。
轉(zhuǎn)載于:https://www.cnblogs.com/lvjygogo/p/8735171.html
總結(jié)
- 上一篇: MongoDB ( 五 )高级_索引
- 下一篇: 爬取校园新闻首页的新闻的详情,使用正则表