執行緒八大基礎核心五(執行緒相關方法一)
1.引子
在java多執行緒併發程式設計中,有八大基礎核心。考考你:
看看都有哪八大基礎核心呢?它們分別是:
1.建立執行緒的方式
2.執行緒啟動
3.執行緒停止
4.執行緒生命週期
5.執行緒相關的方法
6.執行緒相關的屬性
7.執行緒異常處理
8.執行緒安全
今天我們從第五個基礎核心開始:執行緒相關方法
2.考考你
#前情回顧
1.在java程式語言中,與執行緒相關的方法主要有:
1.1.Object.wait/Object.notify/Object/notifyAll
1.2.Thread.sleep/Thread.join/Thread.yield
2.需要把各個方法的作用和使用場景說清楚,需要用兩篇文章進行分享
#考考你
1.你知道wait/notify/notifyAll方法的作用嗎?
2.你知道notify與notifyAll方法的區別嗎?
3.你知道sleep方法的作用嗎?
4.你知道wait與sleep方法的區別嗎?
5.你知道join方法的作用嗎?
6.你知道yield方法的作用嗎?
3.案例:wait/notify/notifyAll
方法簡述:
wait:釋放執行緒擁有的當前鎖物件,讓執行緒進入WAITING狀態
notify:喚醒當前鎖物件等待池(WaitSet)中,某一個執行緒
notifyAll:喚醒當前鎖物件等待池(WaitSet)中,所有執行緒
業務描述:
1.通過wait與notify(notifyAll)實現執行緒協同,實現經典的生產者消費者模式
2.編寫生產者,向佇列中生產資料,如果佇列滿,生產者進行等待,並喚醒消費者進行消費
3.編寫消費者,從佇列中消費資料,如果佇列空,消費者進行等待,並喚醒生產者進行生產
4.在主類main方法中,建立兩個生產者執行緒進行生產
5.在主類main方法中,建立一個消費者執行緒進行消費
生產者:
1 /** 2 * 生產者 3 */ 4 class Producer implements Runnable{ 5 6 public void run() { 7 while(true){ 8 synchronized (ProducerConsumerDemo.LOCK){ 9 // 檢查佇列是否滿,推薦用while而不是if 10 while(ProducerConsumerDemo.QUEUE.size() == 11 ProducerConsumerDemo.CAPACITY){ 12 // 如果佇列滿,則等待消費者消費 13 try { 14 System.out.println("佇列滿size:" +ProducerConsumerDemo.QUEUE.size()+ ",執行緒【" + 15 Thread.currentThread().getName() + "】正在等待消費者消費."); 16 ProducerConsumerDemo.LOCK.wait(); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 } 21 22 // 如果佇列不滿,則正常生產 23 ProducerConsumerDemo.QUEUE.add(1); 24 System.out.println("執行緒【" + Thread.currentThread().getName() + 25 "】生產了資料.當前佇列size:" + ProducerConsumerDemo.QUEUE.size()); 26 27 // 喚醒消費者 28 ProducerConsumerDemo.LOCK.notifyAll(); 29 } 30 } 31 } 32 }
消費者:
1 /** 2 * 消費者 3 */ 4 class Consumer implements Runnable{ 5 6 public void run() { 7 while(true){ 8 synchronized (ProducerConsumerDemo.LOCK){ 9 // 檢查佇列是否空,推薦用while而不是if 10 while(ProducerConsumerDemo.QUEUE.isEmpty()){ 11 // 如果佇列空,則等待生產者生產 12 try { 13 System.out.println("佇列空size:" +ProducerConsumerDemo.QUEUE.size()+ ",執行緒【" + 14 Thread.currentThread().getName() + "】正在等待生產者生產."); 15 ProducerConsumerDemo.LOCK.wait(); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 } 20 21 // 如果佇列不空,則正常消費 22 ProducerConsumerDemo.QUEUE.remove(0); 23 System.out.println("執行緒【" + Thread.currentThread().getName() + 24 "】消費了資料.當前佇列size:" + ProducerConsumerDemo.QUEUE.size()); 25 26 // 喚醒生產者 27 ProducerConsumerDemo.LOCK.notifyAll(); 28 } 29 } 30 } 31 }
完整程式碼:
1 package com.anan.thread.threadmethod; 2 3 import java.util.ArrayList; 4 5 /** 6 * 生產者消費者模型:wait與notify(notifyAll) 7 */ 8 public class ProducerConsumerDemo { 9 10 // 定義公共資源:佇列容量、佇列 11 public final static Integer CAPACITY = 6; 12 public final static ArrayList<Integer> QUEUE = new ArrayList<Integer>(CAPACITY); 13 14 // 定義鎖物件 15 public final static Object LOCK = new Object(); 16 17 public static void main(String[] args) { 18 19 // 建立兩個生產者執行緒 20 Runnable r1 = new Producer(); 21 Thread producer0 = new Thread(r1,"producer-0"); 22 producer0.start(); 23 24 Thread producer1 = new Thread(r1,"producer-1"); 25 producer1.start(); 26 27 // 建立消費者執行緒 28 Runnable r2 = new Consumer(); 29 Thread consumer1 = new Thread(r2,"consumer-0"); 30 consumer1.start(); 31 32 } 33 } 34 35 /** 36 * 生產者 37 */ 38 class Producer implements Runnable{ 39 40 public void run() { 41 while(true){ 42 synchronized (ProducerConsumerDemo.LOCK){ 43 // 檢查佇列是否滿,推薦用while而不是if 44 while(ProducerConsumerDemo.QUEUE.size() == 45 ProducerConsumerDemo.CAPACITY){ 46 // 如果佇列滿,則等待消費者消費 47 try { 48 System.out.println("佇列滿size:" +ProducerConsumerDemo.QUEUE.size()+ ",執行緒【" + 49 Thread.currentThread().getName() + "】正在等待消費者消費."); 50 ProducerConsumerDemo.LOCK.wait(); 51 } catch (InterruptedException e) { 52 e.printStackTrace(); 53 } 54 } 55 56 // 如果佇列不滿,則正常生產 57 ProducerConsumerDemo.QUEUE.add(1); 58 System.out.println("執行緒【" + Thread.currentThread().getName() + 59 "】生產了資料.當前佇列size:" + ProducerConsumerDemo.QUEUE.size()); 60 61 // 喚醒消費者 62 ProducerConsumerDemo.LOCK.notifyAll(); 63 } 64 } 65 } 66 } 67 68 /** 69 * 消費者 70 */ 71 class Consumer implements Runnable{ 72 73 public void run() { 74 while(true){ 75 synchronized (ProducerConsumerDemo.LOCK){ 76 // 檢查佇列是否空,推薦用while而不是if 77 while(ProducerConsumerDemo.QUEUE.isEmpty()){ 78 // 如果佇列空,則等待生產者生產 79 try { 80 System.out.println("佇列空size:" +ProducerConsumerDemo.QUEUE.size()+ ",執行緒【" + 81 Thread.currentThread().getName() + "】正在等待生產者生產."); 82 ProducerConsumerDemo.LOCK.wait(); 83 } catch (InterruptedException e) { 84 e.printStackTrace(); 85 } 86 } 87 88 // 如果佇列不空,則正常消費 89 ProducerConsumerDemo.QUEUE.remove(0); 90 System.out.println("執行緒【" + Thread.currentThread().getName() + 91 "】消費了資料.當前佇列size:" + ProducerConsumerDemo.QUEUE.size()); 92 93 // 喚醒生產者 94 ProducerConsumerDemo.LOCK.notifyAll(); 95 } 96 } 97 } 98 }
執行結果:
4.討論分享
#考考你答案 1.你知道wait/notify/notifyAll方法的作用嗎? 1.1.wait/notify/notifyAll都是Object中的方法 1.2.通過等待與喚醒,實現執行緒之間的協同 1.3.wait方法會釋放當前鎖物件,讓執行緒進入WAITING狀態 1.4.notify方法用於: 1.4.1.喚醒當前鎖物件等待池(WaitSet)中,正在等待 當前鎖物件的某一個執行緒 1.4.2.讓該執行緒進入RUNNABLE狀態,並移入鎖池(EntrySet)中, 重新競爭鎖物件 1.5.notifyAll方法用於: 1.5.1.喚醒當前鎖物件等待池(WaitSet)中,正在等待 當前鎖物件的所有執行緒 1.5.2.讓等待當前鎖物件的所有執行緒,進入RUNNABLE狀態,並 移入鎖池(EntrySet)中,重新競爭鎖物件 2.你知道notify與notifyAll方法的區別嗎? 2.1.通過以上1.4點、1.5點已經說明了notify 與notifyAll的區別 2.2.這裡可能有朋友不明白鎖池(EntrySet),與 等待池(WaitSet)的概念。我們通過一個圖進行說明: #流程文字描述: 1.鎖池(EntrySet):代表正在等待同一鎖物件的執行緒集合,執行緒進入BLOCKED狀態 2.擁有者(Owner):代表已經獲取到鎖物件的執行緒 3.等待池(WaitSet):代表已經進入WAITING狀態,等待被喚醒的執行緒集合 4.流程描述: 4.1.enter:執行緒準備獲取鎖物件,此時鎖被其它執行緒佔有,執行緒進入EntrySet 4.2.acquire:當其它執行緒釋放鎖物件,EntrySet中的某個執行緒獲取佔有鎖物件 4.3.release:佔有鎖物件執行緒,通過wait方式釋放鎖物件,進入WaitSet中,等待被喚醒 4.4.acquire:當有其它執行緒,通過notify/notifyAll方法喚醒WaitSet中執行緒,執行緒將重新進入EntrySet,重新競爭獲取鎖物件 4.5.release and exit:佔有鎖物件執行緒,釋放鎖並退出執行,執行緒生命週期結束
&n