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