java并發(fā)學(xué)習(xí)

參考 我沒有三顆心臟

http://www.itdecent.cn/p/7382c0a843ff
http://www.itdecent.cn/p/cd9d0927be35

線程與進(jìn)程

線程是操作系統(tǒng)中分配和管理資源的基本單位

進(jìn)程是執(zhí)行運(yùn)算的最小單位,線程是進(jìn)程的一個(gè)實(shí)體

線程沒有地址空間,線程包含在進(jìn)程的地址空間中。線程上下文只包含一個(gè)堆棧、一個(gè)寄存器、一個(gè)優(yōu)先權(quán),線程文本包含在他的進(jìn)程的文本片段中,進(jìn)程擁有的所有資源都屬于線程。所有的線程共享進(jìn)程的內(nèi)存和資源。 同一進(jìn)程中的多個(gè)線程共享代碼段(代碼和常量),數(shù)據(jù)段(全局變量和靜態(tài)變量),擴(kuò)展段(堆存儲(chǔ))。但是每個(gè)線程擁有自己的棧段 寄存器的內(nèi)容,棧段又叫運(yùn)行時(shí)段,用來存放所有局部變量和臨時(shí)變量。(線程共享代碼段、數(shù)據(jù)段和堆空間,擁有獨(dú)立的??臻g和寄存器內(nèi)容)

子進(jìn)程不對(duì)任何其他子進(jìn)程施加控制,進(jìn)程的線程可以對(duì)同一進(jìn)程的其它線程施加控制。子進(jìn)程不能對(duì)父進(jìn)程施加控制,進(jìn)程中所有線程都可以對(duì)主線程施加控制。

進(jìn)程切換時(shí),涉及到當(dāng)前進(jìn)程的 CPU 環(huán)境的保存和新被調(diào)度運(yùn)行進(jìn)程的 CPU 環(huán)境的設(shè)置。線程切換僅需要保存和設(shè)置少量的寄存器內(nèi)容,不涉及存儲(chǔ)管理方面的操作。


java多線程實(shí)現(xiàn)方式

extends Thread

繼承Thread類



implements Runnable

實(shí)現(xiàn)Runnable接口


implement Callable

注意run() 方法和 start() 區(qū)別

start() 方法使創(chuàng)建好的線程從runnable狀態(tài)到running狀態(tài);而直接調(diào)用 run() 方法知識(shí)作為一個(gè)普通的方法調(diào)用而已,它只會(huì)在當(dāng)前線程中,串行執(zhí)行 run() 中的代碼

synchronized和ReentrantLock(Lock類)

參考:https://www.cnblogs.com/takumicx/p/9338983.html

.ReentrantLock和synchronized都是獨(dú)占鎖,只允許線程互斥的訪問臨界區(qū)。

synchronized加鎖解鎖的過程是隱式的,用戶不用手動(dòng)操作,優(yōu)點(diǎn)是操作簡(jiǎn)單,但顯得不夠靈活。ReentrantLock需要手動(dòng)加鎖和解鎖,且解鎖的操作盡量要放在finally代碼塊中,保證線程正確釋放鎖。

synchronized因?yàn)榭芍厝胍虼丝梢苑旁诒贿f歸執(zhí)行的方法上,且不用擔(dān)心線程最后能否正確釋放鎖;而ReentrantLock在重入時(shí)要卻確保重復(fù)獲取鎖的次數(shù)必須和重復(fù)釋放鎖的次數(shù)一樣,否則可能導(dǎo)致其他線程無法獲得該鎖。

在創(chuàng)建ReentrantLock的時(shí)候通過傳進(jìn)參數(shù)true創(chuàng)建公平鎖,如果傳入的是false或沒傳參數(shù)則創(chuàng)建的是非公平鎖;而synchronized是非公平鎖

當(dāng)使用synchronized實(shí)現(xiàn)鎖時(shí),阻塞在鎖上的線程除非獲得鎖否則將一直等待下去,也就是說這種無限等待獲取鎖的行為無法被中斷。而ReentrantLock給我們提供了一個(gè)可以響應(yīng)中斷的獲取鎖的方法lockInterruptibly()

使用synchronized結(jié)合Object上的wait和notify方法可以實(shí)現(xiàn)線程間的等待通知機(jī)制。ReentrantLock結(jié)合Condition接口的await()和signal()方法同樣可以實(shí)現(xiàn)這個(gè)功能。而且相比前者使用起來更清晰也更簡(jiǎn)單。

“阻塞”與"非阻塞"? ?"同步"與“異步"

同步和異步關(guān)注的是消息通信機(jī)制

同步,就是在發(fā)出一個(gè)*調(diào)用*時(shí),在沒有得到結(jié)果之前,該*調(diào)用*就不返回。但是一旦調(diào)用返回,就得到返回值了。異步則是相反,*調(diào)用*在發(fā)出之后,這個(gè)調(diào)用就直接返回了,所以沒有返回結(jié)果。換句話說,當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者不會(huì)立刻得到結(jié)果。而是在*調(diào)用*發(fā)出后,*被調(diào)用者*通過狀態(tài)、通知來通知調(diào)用者,或通過回調(diào)函數(shù)處理這個(gè)調(diào)用。

阻塞和非阻塞關(guān)注的是程序在等待調(diào)用結(jié)果(消息,返回值)時(shí)的狀態(tài).

阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起。調(diào)用線程只有在得到結(jié)果之后才會(huì)返回。非阻塞調(diào)用指在不能立刻得到結(jié)果之前,該調(diào)用不會(huì)阻塞當(dāng)前線程。


線程5大狀態(tài)

new/Runnable/Running/Blocked/Dead

狀態(tài)及轉(zhuǎn)移關(guān)系

注:線程阻塞情況有多種

(一). 等待阻塞:運(yùn)行(running)的線程執(zhí)行o.wait()方法,JVM會(huì)把該線程放入等待隊(duì)列(waitting queue)中。

(二). 同步阻塞:運(yùn)行(running)的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則JVM會(huì)把該線程放入鎖池(lock pool)中。

(三). 其他阻塞:運(yùn)行(running)的線程執(zhí)行Thread.sleep(long ms)或t.join()方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入可運(yùn)行(runnable)狀態(tài)

并發(fā)(Concurrency)和并行(Parallelism)的區(qū)別

并行性是指兩個(gè)或多個(gè)事件在同一時(shí)刻發(fā)生。而并發(fā)性是指連個(gè)或多個(gè)事件在同一時(shí)間間隔內(nèi)發(fā)生。

Monitor機(jī)制


當(dāng)一個(gè)線程需要訪問受保護(hù)的數(shù)據(jù)(即需要獲取對(duì)象的Monitor)時(shí),它會(huì)首先在entry-set入口隊(duì)列中排隊(duì)(這里并不是真正的按照排隊(duì)順序),如果沒有其他線程正在持有對(duì)象的Monitor,那么它會(huì)和entry-set隊(duì)列和wait-set隊(duì)列中的被喚醒的其他線程進(jìn)行競(jìng)爭(zhēng)(即通過CPU調(diào)度),選出一個(gè)線程來獲取對(duì)象的Monitor,執(zhí)行受保護(hù)的代碼段,執(zhí)行完畢后釋放Monitor,如果已經(jīng)有線程持有對(duì)象的Monitor,那么需要等待其釋放Monitor后再進(jìn)行競(jìng)爭(zhēng)。

再說一下wait-set隊(duì)列。當(dāng)一個(gè)線程擁有Monitor后,經(jīng)過某些條件的判斷(比如用戶取錢發(fā)現(xiàn)賬戶沒錢),這個(gè)時(shí)候需要調(diào)用Object的wait方法,線程就釋放了Monitor,進(jìn)入wait-set隊(duì)列,等待Object的notify方法(比如用戶向賬戶里面存錢)。當(dāng)該對(duì)象調(diào)用了notify方法或者notifyAll方法后,wait-set中的線程就會(huì)被喚醒,然后在wait-set隊(duì)列中被喚醒的線程和entry-set隊(duì)列中的線程一起通過CPU調(diào)度來競(jìng)爭(zhēng)對(duì)象的Monitor,最終只有一個(gè)線程能獲取對(duì)象的Monitor。

通過上圖可以得知,調(diào)用obj.wait()時(shí),當(dāng)前線程必須獲取到了obj的Monitor。即wait必須放在同步方法或同步代碼塊中。

調(diào)用wait方法,就意味著釋放了Monitor;調(diào)用notify方法,并不意味著釋放了Monitor,必須要等同步代碼塊結(jié)束后才會(huì)釋放Monitor。

Volatile關(guān)鍵字

保證變量的可見性:將修改的值立即寫入主存,對(duì)值的修改是其他線程工作內(nèi)存的stop緩存失效,使得其他線程去主存讀取更新自己的緩存。

禁止指令重排:插入內(nèi)存屏障(內(nèi)存屏障????


線程池

包括工作線程和等待隊(duì)列

如果運(yùn)行的線程少于 corePoolSize,則 Executor 始終首選添加新的線程,而不進(jìn)行排隊(duì);如果運(yùn)行的線程等于或者多于 corePoolSize,則 Executor 始終首選將請(qǐng)求加入隊(duì)列,而不是添加新線程;如果等待隊(duì)列已滿,但是當(dāng)前線程大小小于maxinumPoolSize,創(chuàng)建新的線程;如果核心線程數(shù)、等待隊(duì)列、最大線程數(shù)都滿,則使用handler處理任務(wù)。

handler策略:1.AbortPolicy 拋出異常? 2.DiscardPolicy直接丟棄? ? 3.DiscardOldestPolicy 丟棄隊(duì)列中最舊的? ? 4.CallerRunsPolicy 不用線程池中的線程,用調(diào)用者所在線程執(zhí)行。

線程池的好處:1.減少創(chuàng)建和銷毀線程的次數(shù),每個(gè)工作線程都可以被重復(fù)利用,可執(zhí)行多個(gè)任務(wù)? ? 2.提高響應(yīng)速度,不需要等待線程創(chuàng)立即可執(zhí)行? ? 3.提高線程的可管理性? ? 4.避免創(chuàng)建大量線程,導(dǎo)致消耗系統(tǒng)資源。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容