上一篇:Java線程(二)
上一篇講述了線程的互斥(同步),但是在很多情況下,僅僅同步是不夠的,還需要線程與線程協作(通訊),生產者/消費者模式是一個經典的線程同步以及通訊的模型。
假設有這樣一種情況,有一個盤子,盤子裡只能放一個雞蛋,A線程專門往盤子裡放雞蛋,如果盤子裡有雞蛋,則一直等到盤子裡沒雞蛋,B線程專門從盤子裡取雞蛋,如果盤子裡沒雞蛋,則一直等到盤子裡有雞蛋。這裡盤子是一個互斥區,每次放雞蛋是互斥的,每次取雞蛋也是互斥的,A線程放雞蛋,如果這時B線程要取雞蛋,由於A沒有釋放鎖,B線程處於等待狀態,進入阻塞隊列,放雞蛋之後,要通知B線程取雞蛋,B線程進入就緒隊列,反過來,B線程取雞蛋,如果A線程要放雞蛋,由於B線程沒有釋放鎖,A線程處於等待狀態,進入阻塞隊列,取雞蛋之後,要通知A線程放雞蛋,A線程進入就緒隊列。我們希望當盤子裡有雞蛋時,A線程阻塞,B線程就緒,盤子裡沒雞蛋時,A線程就緒,B線程阻塞,代碼如下:
import java.util.ArrayList;import java.util.List;/** 定義一個盤子類,可以放雞蛋和取雞蛋 */public class Plate {/** 裝雞蛋的盤子 */List<Object> eggs = new ArrayList<Object>();/** 取雞蛋 */public synchronized Object getEgg() {while (eggs.size() == 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}Object egg = eggs.get(0);eggs.clear();// 清空盤子notify();// 喚醒阻塞隊列的某線程到就緒隊列System.out.println("拿到雞蛋");return egg;}/** 放雞蛋 */public synchronized void putEgg(Object egg) {while (eggs.size() > 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}eggs.add(egg);// 往盤子裡放雞蛋notify();// 喚醒阻塞隊列的某線程到就緒隊列System.out.println("放入雞蛋");}static class AddThread extends Thread {private Plate plate;private Object egg = new Object();public AddThread(Plate plate) {this.plate = plate;}public void run() {plate.putEgg(egg);}}static class GetThread extends Thread {private Plate plate;public GetThread(Plate plate) {this.plate = plate;}public void run() {plate.getEgg();}}public static void main(String args[]) {Plate plate = new Plate();for(int i = 0; i < 10; i++) {new Thread(new AddThread(plate)).start();new Thread(new GetThread(plate)).start();}}}
輸出結果:
放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋放入雞蛋拿到雞蛋
8 l程式開始,A線程判斷盤子是否為空白,放入一個雞蛋,並且喚醒在阻塞隊列的一個線程,阻塞隊列為空白;假設CPU又調度了一個A線程,盤子非空,執行等待,這個A線程進入阻塞隊列;然後一個B線程執行,盤子非空,取走雞蛋,並喚醒阻塞隊列的A線程,A線程進入就緒隊列,此時就緒隊列就一個A線程,馬上執行,放入雞蛋;如果再來A線程重複第一步,在來B線程重複第二步,整個過程就是生產者(A線程)生產雞蛋,消費者(B線程)消費雞蛋。
前段時間看了張孝祥老師線程的視頻,講述了一個其學員的面試題,也是線程通訊的,在此也分享一下。
題目:子線程迴圈10次,主線程迴圈100次,如此迴圈100次,好像是空中網的筆試題。
public class ThreadTest2 {public static void main(String[] args) {final Business business = new Business();new Thread(new Runnable() {@Overridepublic void run() {threadExecute(business, "sub");}}).start();threadExecute(business, "main");}public static void threadExecute(Business business, String threadType) {for(int i = 0; i < 100; i++) {try {if("main".equals(threadType)) {business.main(i);} else {business.sub(i);}} catch (InterruptedException e) {e.printStackTrace();}}}}class Business {private boolean bool = true;public synchronized void main(int loop) throws InterruptedException {while(bool) {this.wait();}for(int i = 0; i < 100; i++) {System.out.println("main thread seq of " + i + ", loop of " + loop);}bool = true;this.notify();}public synchronized void sub(int loop) throws InterruptedException {while(!bool) {this.wait();}for(int i = 0; i < 10; i++) {System.out.println("sub thread seq of " + i + ", loop of " + loop);}bool = false;this.notify();}}
大家注意到沒有,在調用wait方法時,都是用while判斷條件的,而不是if,在wait方法說明中,也推薦使用while,因為在某些特定的情況下,線程有可能被假喚醒,使用while會迴圈檢測更穩妥。
下一篇:Java線程(四)
本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7433673。