java線程經典之生產者與消費者
---------------------- ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! ----------------------
無論是課本還是視頻教學都會有這麼一個例子生產者與消費者,可以說是一個經典例子了!
生產者-消費者(producer-consumer)問題,也稱作有界緩衝區(bounded-buffer)問題,兩個進程共享一個公共的固定大小的緩衝區。其中一個是生產者,用於將消息放入緩衝區;另外一個是消費者,用於從緩衝區中取出消息。問題出現在當緩衝區已經滿了,而此時生產者還想向其中放入一個新的數據項的情形,其解決方法是讓生產者此時進行休眠,等待消費者從緩衝區中取走了一個或者多個數據後再去喚醒它。同樣地,當緩衝區已經空了,而消費者還想去取消息,此時也可以讓消費者進行休眠,等待生產者放入一個或者多個數據時再喚醒它。
一,首先定義公共資源類,其中的變量number是保存的公共數據。並且定義兩個方法,增加number的值和減少number的值。由於多線程的原因,必須加上synchronized關鍵字,注意while判斷的條件。
Java代碼
1 /**
2 * 公共資源類
3 */
4 public class PublicResource {
5 private int number = 0;
6
7 /**
8 * 增加公共資源
9 */
10 public synchronized void increace() {
11 while (number != 0) {
12 try {
13 wait();
14 } catch (InterruptedException e) {
15 e.printStackTrace();
16 }
17 }
18 number++;
19 System.out.println(number);
20 notify();
21 }
22
23 /**
24 * 減少公共資源
25 */
26 public synchronized void decreace() {
27 while (number == 0) {
28 try {
29 wait();
30 } catch (InterruptedException e) {
31 e.printStackTrace();
32 }
33 }
34 number--;
35 System.out.println(number);
36 notify();
37 }
38 }
/**
* 公共資源類
*/
public class PublicResource {
private int number = 0;
/**
* 增加公共資源
*/
public synchronized void increace() {
while (number != 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(number);
notify();
}
/**
* 減少公共資源
*/
public synchronized void decreace() {
while (number == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
}
二,分別定義生產者線程和消費者線程,並模擬多次生產和消費,即增加和減少公共資源的number值
Java代碼
39 /**
40 * 生產者線程,負責生產公共資源
41 */
42 public class ProducerThread implements Runnable {
43 private PublicResource resource;
44
45 public ProducerThread(PublicResource resource) {
46 this.resource = resource;
47 }
48
49 @Override
50 public void run() {
51 for (int i = 0; i < 10; i++) {
52 try {
53 Thread.sleep((long) (Math.random() * 1000));
54 } catch (InterruptedException e) {
55 e.printStackTrace();
56 }
57 resource.increace();
58 }
59 }
60 }
61 /**
62 * 消費者線程,負責消費公共資源
63 */
64 public class ConsumerThread implements Runnable {
65 private PublicResource resource;
66
67 public ConsumerThread(PublicResource resource) {
68 this.resource = resource;
69 }
70
71 @Override
72 public void run() {
73 for (int i = 0; i < 10; i++) {
74 try {
75 Thread.sleep((long) (Math.random() * 1000));
76 } catch (InterruptedException e) {
77 e.printStackTrace();
78 }
79 resource.decreace();
80 }
81 }
82 }
/**
* 生產者線程,負責生產公共資源
*/
public class ProducerThread implements Runnable {
private PublicResource resource;
public ProducerThread(PublicResource resource) {
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.increace();
}
}
}
/**
* 消費者線程,負責消費公共資源
*/
public class ConsumerThread implements Runnable {
private PublicResource resource;
public ConsumerThread(PublicResource resource) {
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.decreace();
}
}
}
三,模擬多個生產者和消費者操作公共資源的情形,結果須保證是在允許的範圍內。
Java代碼
83 public class ProducerConsumerTest {
84 public static void main(String[] args) {
85 PublicResource resource = new PublicResource();
86 new Thread(new ProducerThread(resource)).start();
87 new Thread(new ConsumerThread(resource)).start();
88 new Thread(new ProducerThread(resource)).start();
89 new Thread(new ConsumerThread(resource)).start();
90 new Thread(new ProducerThread(resource)).start();
91 new Thread(new ConsumerThread(resource)).start();
92 }
93 }
我的總結:生產者和消費者
if判斷標記,只有一次,會導致不該運行的線程運行了,出現了數據錯誤的情況。
while判斷標記,解決了線程獲取執行權後,是否要運行!
notify:只能喚醒以一個線程,如果本方喚醒了本方,就沒有意義。而且while判斷標記+notify會導致死鎖。
notifyAll 解決了本方線程一定會喚醒對方線程的問題。
---------------------- ASP.Net+Android+IOS開發、.Net培訓、期待與您交流! ----------------------