1. 程式人生 > >執行緒八大基礎核心五(執行緒相關方法一)

執行緒八大基礎核心五(執行緒相關方法一)

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