JAVA線程6 - 線程間的通訊

1、概述

線程之間須要一些協調通訊,來共同完成一件任務。 java

2、線程間的通訊

1. 線程交互

多個線程在處理同一資源,可是任務卻不一樣。 編程

其中涉及的三個方法 : 多線程

wait():讓線程處於阻塞狀態,被wait的線程會被存儲到線程池中。 
notify():隨機喚醒一個線程池中的線程。 

notifyAll():喚醒線程池中的全部線程。  併發

這些方法都是final的,即它們都是不能被重寫的,不能經過子類覆寫去改變它們的行爲。
編程語言

關於等待/喚醒機制,要記住的關鍵點是:
必須從同步環境內調用wait()、notify()、notifyAll()方法。線程不能調用對象上等待或通知的方法,除非它擁有那個對象的鎖。
wait()、notify()、notifyAll()都是Object的實例方法。與每一個對象具備鎖同樣,每一個對象能夠有一個線程列表,他們等待來自該信號(通知)。線程經過執行對象上的wait()方法得到這個等待列表。從那時候起,它再也不執行任何其餘指令,直到調用對象的notify()方法爲止。若是多個線程在同一個對象上等待,則將只選擇一個線程(不保證以何種順序)繼續執行。若是沒有線程等待,則不採起任何特殊操做。 測試

當在對象上調用wait()方法時,執行該代碼的線程當即放棄它在對象上的鎖。然而調用notify()時,並不意味着這時線程會放棄其鎖。若是線程仍然在完成同步代碼,則線程在移出以前不會放棄鎖。所以,只要調用notify()並不意味着這時該鎖變得可用。 優化

2. 生產者消費者

無論任何編程語言,對於多線程程序來講,生產者和消費者模型都是最經典的。 this

實際上,準確說應該是「生產者-消費者-倉儲」模型,離開了倉儲,生產者消費者模型就顯得沒有說服力了。
對於此模型,應該明確一下幾點:
一、生產者僅僅在倉儲未滿時候生產,倉滿則中止生產。
二、消費者僅僅在倉儲有產品時候才能消費,倉空則等待。
三、當消費者發現倉儲沒產品可消費時候會通知生產者生產。
四、生產者在生產出可消費產品時候,應該通知等待的消費者去消費。 spa


/** 
* Java線程:併發協做-生產者消費者模型 
* 
* @author leizhimin 2009-11-4 14:54:36 
*/ 
public class Test { 
    public static void main(String[] args) { 
        Godown godown = new Godown(30); 
        Consumer c1 = new Consumer(50, godown); 
        Consumer c2 = new Consumer(20, godown); 
        Consumer c3 = new Consumer(30, godown); 
        Producer p1 = new Producer(10, godown); 
        Producer p2 = new Producer(10, godown); 
        Producer p3 = new Producer(10, godown); 
        Producer p4 = new Producer(10, godown); 
        Producer p5 = new Producer(10, godown); 

        c1.start(); 
        c2.start(); 
        c3.start(); 
        p1.start(); 
        p2.start(); 
        p3.start(); 
        p4.start(); 
        p5.start(); 
    } 
} 

/** 
* 倉庫 
*/ 
class Godown { 
    public static final int max_size = 100; //最大庫存量 
    public int curnum;     //當前庫存量 

    Godown() { 
    } 

    Godown(int curnum) { 
        this.curnum = curnum; 
    } 

    /** 
     * 生產指定數量的產品 
     * 
     * @param neednum 
     */ 
    public synchronized void produce(int neednum) { 
        //測試是否須要生產 
        while (neednum + curnum > max_size) { 
            System.out.println("要生產的產品數量" + neednum + "超過剩餘庫存量" + (max_size - curnum) + ",暫時不能執行生產任務!"); 
            try { 
                //當前的生產線程等待 
                wait(); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
        //知足生產條件,則進行生產,這裏簡單的更改當前庫存量 
        curnum += neednum; 
        System.out.println("已經生產了" + neednum + "個產品,現倉儲量爲" + curnum); 
        //喚醒在此對象監視器上等待的全部線程 
        notifyAll(); 
    } 

    /** 
     * 消費指定數量的產品 
     * 
     * @param neednum 
     */ 
    public synchronized void consume(int neednum) { 
        //測試是否可消費 
        while (curnum < neednum) { 
            try { 
                //當前的生產線程等待 
                wait(); 
            } catch (InterruptedException e) { 
                e.printStackTrace(); 
            } 
        } 
        //知足消費條件,則進行消費,這裏簡單的更改當前庫存量 
        curnum -= neednum; 
        System.out.println("已經消費了" + neednum + "個產品,現倉儲量爲" + curnum); 
        //喚醒在此對象監視器上等待的全部線程 
        notifyAll(); 
    } 
} 

/** 
* 生產者 
*/ 
class Producer extends Thread { 
    private int neednum;                //生產產品的數量 
    private Godown godown;            //倉庫 

    Producer(int neednum, Godown godown) { 
        this.neednum = neednum; 
        this.godown = godown; 
    } 

    public void run() { 
        //生產指定數量的產品 
        godown.produce(neednum); 
    } 
} 

/** 
* 消費者 
*/ 
class Consumer extends Thread { 
    private int neednum;                //生產產品的數量 
    private Godown godown;            //倉庫 

    Consumer(int neednum, Godown godown) { 
        this.neednum = neednum; 
        this.godown = godown; 
    } 

    public void run() { 
        //消費指定數量的產品 
        godown.consume(neednum); 
    } 
}

該例中使用了notifyAll()方法喚醒了全部方法,其實最好只喚醒對方便可。 咱們將在後期章節中對其優化。  線程

相關文章
相關標籤/搜索