生產者和消費者是一種經典的供求案例,生產者和消費者線程之間的關係涉及線程之間的交互。生產者線程產生的數據項,將來會被消費者消費,每個生產出來的數據會被保存在一個共享的倉庫中。
假設線程以不同的速度進行,那麼將會導致消費者可能獲取到還未生產的數據,生產者也有可能在消費者還未取走前一條數據的前提下繼續生產,這樣就會造成混亂。
爲了克服這種混亂,生產者線程必須等待,知道消費者取走之後通知生產者,等待解除,可以生產。如果生產者未生產數據,消費者就要一直等待,直到生產者生產完畢,喚醒消費者線程,取走生產好的數據。
以下爲代碼實現:
package my_application;
public class ProducerConsumer {
public static void main(String[] args) {
ShareStorage s = new ShareStorage();
new Producer(s).start();
new Consumer(s).start();
}
}
class ShareStorage{
private char c;
//此實例變量跟蹤兩個條件,生產者等待消費者消費一個數據,消費者等待生產者生產一個數據
private volatile boolean flag = true;
synchronized void setShareData(char c){
while (!flag) {//消費者未取走,則生產者等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.c = c;
flag = false;//設置標誌位爲false,告訴消費者可以消費,此時生產者等待
notify();
}
synchronized char getShareData(){
while (flag) {//flag = true ,表示消費者等待,此時生產者生產
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = true;
notify();
return c;
}
}
class Producer extends Thread{
private final ShareStorage shareStorage;
Producer(ShareStorage shareStorage){
this.shareStorage = shareStorage;
}
@Override
public void run() {
for (char ch = 'A'; ch < 'Z'; ch++) {
shareStorage.setShareData(ch);
System.out.println(ch + ",生產者生產");
}
}
}
class Consumer extends Thread{
private final ShareStorage shareStorage;
Consumer(ShareStorage shareStorage){
this.shareStorage = shareStorage;
}
@Override
public void run() {
char ch;
do {
ch = shareStorage.getShareData();
System.out.println(ch + ",消費者消費");
} while (ch != 'Z');
}
}
此時的輸出結果可能爲:
O,生產者生產
O,消費者消費
P,生產者生產
P,消費者消費
或者
A,生產者生產
B,生產者生產
A,消費者消費
B,消費者消費
這樣的輸出並不表示消費者和生產者線程沒有同步,這是因爲setShareData(),getShareData()之後的輸出沒有被同步造成的結果,爲了解決這個問題,加上同步塊就可以完美解決這個問題了。見下面修改後的代碼:
package my_application;
public class ProducerConsumer {
public static void main(String[] args) {
ShareStorage s = new ShareStorage();
new Producer(s).start();
new Consumer(s).start();
}
}
class ShareStorage{
private char c;
private volatile boolean flag = true;//寫入取走標記
synchronized void setShareData(char c){
while (!flag) {//消費者未取走,則生產者等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.c = c;
flag = false;//設置標誌位爲false,告訴消費者可以消費,此時生產者等待
notify();
}
synchronized char getShareData(){
while (flag) {//flag = true ,表示消費者等待,此時生產者生產
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag = true;
notify();
return c;
}
}
class Producer extends Thread{
private final ShareStorage shareStorage;
Producer(ShareStorage shareStorage){
this.shareStorage = shareStorage;
}
@Override
public void run() {
for (char ch = 'A'; ch < 'Z'; ch++) {
synchronized (shareStorage) {
shareStorage.setShareData(ch);
System.out.println(ch + ",生產者生產");
}
}
}
}
class Consumer extends Thread{
private final ShareStorage shareStorage;
Consumer(ShareStorage shareStorage){
this.shareStorage = shareStorage;
}
@Override
public void run() {
char ch;
do {
synchronized (shareStorage) {
ch = shareStorage.getShareData();
System.out.println(ch + ",消費者消費");
}
} while (ch != 'Z');
}
}
輸出結果:
A,生產者生產
A,消費者消費
B,生產者生產
B,消費者消費
C,生產者生產
C,消費者消費
D,生產者生產
D,消費者消費
E,生產者生產
E,消費者消費
...