摘要:請(qǐng)參看前一篇文章并發(fā)學(xué)習(xí)筆記一原子性可見(jiàn)性有序性問(wèn)題六等待通知機(jī)制什么是等待通知機(jī)制當(dāng)線程不滿足某個(gè)條件,則進(jìn)入等待狀態(tài)如果線程滿足要求的某個(gè)條件后,則通知等待的線程重新執(zhí)行。經(jīng)極客時(shí)間并發(fā)編程實(shí)戰(zhàn)專欄內(nèi)容學(xué)習(xí)整理
請(qǐng)參看前一篇文章:Java 并發(fā)學(xué)習(xí)筆記(一)——原子性、可見(jiàn)性、有序性問(wèn)題
六、等待—通知機(jī)制什么是等待通知—機(jī)制?當(dāng)線程不滿足某個(gè)條件,則進(jìn)入等待狀態(tài);如果線程滿足要求的某個(gè)條件后,則通知等待的線程重新執(zhí)行。
等待通知機(jī)制的流程一般是這樣的:線程首先獲取互斥鎖,當(dāng)不滿足某個(gè)條件的時(shí)候,釋放互斥鎖,并進(jìn)入這個(gè)條件的等待隊(duì)列;一直等到滿足了這個(gè)條件之后,通知等待的線程,并且需要重新獲取互斥鎖。
等待-通知機(jī)制可以使用 Java 的 synchronized 關(guān)鍵字,配合 wait()、notify()、notifyAll() 這個(gè)三個(gè)方法來(lái)實(shí)現(xiàn)。
前面說(shuō)到的解決死鎖問(wèn)題的那個(gè)例子,一次性申請(qǐng)所有的資源,使用的是循環(huán)等待,這在并發(fā)量很大的時(shí)候比較消耗 CPU 資源。
現(xiàn)在使用等待-通知機(jī)制進(jìn)行優(yōu)化:
final class Monitor { private List
1) 每個(gè)互斥鎖都有相應(yīng)的等待隊(duì)列,例如上面的例子,就存在兩個(gè)等待隊(duì)列,一是 synchronized 入口等待隊(duì)列,二是 while 循環(huán)這個(gè)條件的等待隊(duì)列。
2) 調(diào)用 wait() 方法,會(huì)使當(dāng)前線程釋放持有的鎖,并進(jìn)入這個(gè)條件的等待隊(duì)列。滿足條件之后,隊(duì)列中的線程被喚醒,不是馬上執(zhí)行,而是需要重新獲取互斥鎖。例如上圖中,if 條件的隊(duì)列中的線程被喚醒后,需要重新進(jìn)入 synchronized 處獲取互斥鎖。
相同點(diǎn):兩個(gè)方法都會(huì)讓渡 CPU 的使用權(quán),等待再次被調(diào)度。
不同點(diǎn):
wait 屬于 Object 的方法,sleep 是 Thread 的方法
wait 只能在同步方法或同步塊中調(diào)用,sleep 可以在任何地方調(diào)用
wait 會(huì)釋放線程持有的鎖,sleep 不會(huì)釋放鎖資源
七、管程理論指的是對(duì)共享變量和對(duì)共享變量的操作的管理,使其支持并發(fā),對(duì)應(yīng)到 Java,指的是管理類的成員變量和方法,讓這個(gè)類是線程安全的。
管程主要的模型有 Hasen、Hoare、MESA ,其中 MESA 最常用。管程的 MESA 模型主要解決的是線程的互斥和同步問(wèn)題,和上面說(shuō)到的等待-通知機(jī)制十分類似。示意圖如下:
首先看看管程是如何實(shí)現(xiàn)互斥的?在管程的入口有一個(gè)等待隊(duì)列,一次只允許一個(gè)線程進(jìn)入管程。每個(gè)條件對(duì)應(yīng)一個(gè)等待隊(duì)列,當(dāng)線程不滿足條件的時(shí)候,進(jìn)入對(duì)應(yīng)的等待隊(duì)列;當(dāng)條件滿足的時(shí)候,隊(duì)列中的線程被喚醒,重新進(jìn)入到入口處的等待隊(duì)列獲取互斥鎖,這就實(shí)現(xiàn)了線程的同步問(wèn)題。
接下來(lái)使用代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的阻塞隊(duì)列,這就是一個(gè)很典型的管程模型,解決了線程互斥和同步問(wèn)題。
public class BlockingQueue八、Java 中的線程{ private int capacity; private int size; private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); /** * 入隊(duì)列 */ public void enqueue(T data){ lock.lock(); try { //如果隊(duì)列滿了,需要等待,直到隊(duì)列不滿 while (size >= capacity){ notFull.await(); } //入隊(duì)代碼,省略 //入隊(duì)之后,通知隊(duì)列已經(jīng)不為空了 notEmpty.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } /** * 出隊(duì)列 */ public T dequeue(){ lock.lock(); try { //如果隊(duì)列為空,需要等待,直到隊(duì)列不為空 while (size <= 0){ notEmpty.await(); } //出隊(duì)代碼,省略 //出隊(duì)列之后,通知隊(duì)列已經(jīng)不滿了 notFull.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } //實(shí)際應(yīng)該返回出隊(duì)數(shù)據(jù) return null; } }
Java 中的線程共分為了 6 種狀態(tài),分別是:
NEW(初始化狀態(tài))
RUNNABLE(可運(yùn)行/運(yùn)行狀態(tài))
BLOCKED(阻塞狀態(tài))
WAITING(無(wú)限時(shí)等待)
TIMED_WAITING(限時(shí)等待)
TERMINATED(終止?fàn)顟B(tài))
RUNNABLE 與 BLOCKED 狀態(tài)的轉(zhuǎn)換:在線程等待 synchronized 的鎖時(shí),會(huì)進(jìn)入 BLOCKED 狀態(tài),當(dāng)獲取到鎖之后,又轉(zhuǎn)換到 RUNNABLE 狀態(tài)。
RUNNABLE 與 WAITING 狀態(tài)的轉(zhuǎn)換:1) 線程獲取到 synchronized 鎖之后,并且調(diào)用了 wait() 方法。 2) 調(diào)用 Thread.join() 方法,例如線程 A 調(diào)用 join() 方法,線程 B 等待 A 執(zhí)行完畢,等待期間 B 進(jìn)入 WAITING 狀態(tài),線程 A 執(zhí)行完后,線程 B 切換到 RUNNABLE 狀態(tài)。3) 調(diào)用 LockSupport.park() 方法
RUNNABLE 與 TIMED_WAITING 狀態(tài)的轉(zhuǎn)換:以上三種情況,分別在方法中加上超時(shí)參數(shù)即可。另外還有兩種情況:Thread.sleep(long millis) 方法,LockSupprt.parkNanos(Object blocker, long deadline)。
NEW 到 RUNNABLE 狀態(tài)的轉(zhuǎn)換:在 Java 中新創(chuàng)建的線程,會(huì)立即進(jìn)入 NEW 狀態(tài),然后啟動(dòng)線程進(jìn)入 RUNNABLE 狀態(tài)。Java 中新建線程一般有三種方式:
繼承 Thread 類
public class MyThread extends Thread { @Override public void run() { System.out.println("I am roseduan"); } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } }
實(shí)現(xiàn) Runnable 接口,并將其實(shí)現(xiàn)類傳給 Thread 作為參數(shù)
public class MyThread { public static void main(String[] args) { Thread thread = new Thread(new Print()); thread.start(); } } class Print implements Runnable{ @Override public void run() { System.out.println("I am roseduan"); } }
實(shí)現(xiàn) Collable 接口,將其實(shí)現(xiàn)類傳給線程池執(zhí)行,并且可以獲取返回結(jié)果
public class ThreadTest { public static void main(String[] args) throws InterruptedException { //線程池 BlockingQueuequeue = new LinkedBlockingQueue<>(5); ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 1, TimeUnit.HOURS, queue); //執(zhí)行 Future> submit = threadPool.submit(new Demo()); } } class Demo implements Callable { @Override public String call() { System.out.println("I am roseduan"); return "I am roseduan"; } }
NEW 到 TERMINATED 狀態(tài)的轉(zhuǎn)換:線程執(zhí)行完 run() 方法后,會(huì)自動(dòng)切換到 TERMINATED 狀態(tài)。如果手動(dòng)中止線程,可以使用 interrupt() 方法。
局部變量存在于方法中,每個(gè)方法都有對(duì)應(yīng)的調(diào)用棧幀,由于每個(gè)線程都有自己獨(dú)立的方法調(diào)用棧,因此局部變量并沒(méi)有被共享。所以即便多個(gè)線程同時(shí)調(diào)用同一個(gè)方法,方法內(nèi)部的局部變量也是線程安全的,不需要多帶帶加鎖。
經(jīng)極客時(shí)間《Java 并發(fā)編程實(shí)戰(zhàn)》專欄內(nèi)容學(xué)習(xí)整理
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/77718.html
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對(duì)象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對(duì)象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
摘要:基礎(chǔ)問(wèn)題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識(shí)點(diǎn)總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時(shí)間和時(shí)間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對(duì)象鎖和類鎖的區(qū)別,,優(yōu)缺點(diǎn)及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問(wèn)題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...
摘要:再附一部分架構(gòu)面試視頻講解本文已被開(kāi)源項(xiàng)目學(xué)習(xí)筆記總結(jié)移動(dòng)架構(gòu)視頻大廠面試真題項(xiàng)目實(shí)戰(zhàn)源碼收錄 Java反射(一)Java反射(二)Java反射(三)Java注解Java IO(一)Java IO(二)RandomAccessFileJava NIOJava異常詳解Java抽象類和接口的區(qū)別Java深拷貝和淺拷...
閱讀 1005·2023-04-26 02:21
閱讀 2822·2021-09-24 09:47
閱讀 1612·2019-08-30 15:55
閱讀 2169·2019-08-30 14:01
閱讀 2327·2019-08-29 14:01
閱讀 2052·2019-08-29 12:46
閱讀 819·2019-08-26 13:27
閱讀 1940·2019-08-26 12:23