2.2.線程的同步和協作_基本協作

基本協作


併發編程中有時需要線程間進行協作,典型的問題是生產者-消費者問題:有一個數據緩衝區,一個或多個生產者將數據存入這個緩衝區,一個或多個數據消費者將數據從緩衝區中取走。這裏有兩個問題要解決:

  1. 緩衝區是一個共享數據結構,必須使用同步機制控制對它的訪問,可以使用上一章節介紹的synchronized關鍵字進行同步。
  2. 緩衝區的數據是有限的,如果當緩衝區滿了生產者就不能再放入數據,如果緩衝區空了消費者就不能讀取數據。這是本文討論的生產者和消費者之間的協作問題。

Java裏線程協作的基本方式是通過Object對象提供的waitnotifynotifyAll方法,這三個方法都需要在同步代碼塊中調用,否則JVM將拋出IllegalMonitorStateException。當線程調用wait方法時,JVM將這個線程置入休眠,並且釋放控制這個同步代碼塊的對象,同時允許其他線程執行這個對象控制的其他同步代碼塊。爲了喚醒這個線程,必須在這個對象控制的某個同步代碼塊中調用notify或者notifyAll方法。


下面以生產者-消費者示例來說明synchronized、wait、notify、notifyAll的使用:

public class ProducerConsumerDemo {

    public static void main(String[] args){
        DataStorage storage = new DataStorage(10);

        Runnable consumer = new Consumer(storage);
        Runnable producer = new Producer(storage);

        Thread pt1 = new Thread(producer);
        Thread ct1 = new Thread(consumer);

        System.out.println("main:啓動1個生產者");
        pt1.start();;
        System.out.println("main:啓動1個消費者");
        ct1.start();

        System.out.println("main:等待線程完成");
        try {
            pt1.join();
            ct1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main:退出");
    }
}

/** 消費者**/
class Consumer implements Runnable{

    private DataStorage storage;

    public Consumer(DataStorage storage){
        this.storage = storage;
    }

    @Override
    public void run() {
        for(int i=0; i<20; i++){
            storage.get();
        }
    }
}
/** 生產者**/
class Producer implements Runnable{

    private DataStorage storage;
    public Producer(DataStorage storage){
        this.storage = storage;
    }

    @Override
    public void run() {
        for(int i=0; i<20; i++){
            storage.put();
        }
    }
}

/** 公共數據存儲類 **/
class DataStorage{
    private int maxSize;
    private List<Date> storage;

    public DataStorage(int max){
        this.maxSize = max;
        this.storage = new LinkedList<Date>();
    }

    public synchronized void put(){
        if(this.storage.size() == maxSize){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.storage.add(new Date());
        System.out.println("生產。數據數量:" + storage.size());
        notifyAll();
    }

    public synchronized Date get(){
        if(this.storage.size() == 0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Date data = ((LinkedList<Date>)this.storage).poll();
        System.out.println("消費。數據數量:" + storage.size());
        notifyAll();
        return data;
    }
}

程序運行日誌:
main:啓動1個生產者
main:啓動1個消費者
main:等待線程完成
生產。數據數量:1
生產。數據數量:2
生產。數據數量:3
生產。數據數量:4
生產。數據數量:5
生產。數據數量:6
生產。數據數量:7
生產。數據數量:8
生產。數據數量:9
生產。數據數量:10
消費。數據數量:9
消費。數據數量:8
消費。數據數量:7
消費。數據數量:6
消費。數據數量:5
消費。數據數量:4
消費。數據數量:3
消費。數據數量:2
消費。數據數量:1
消費。數據數量:0
生產。數據數量:1
消費。數據數量:0
生產。數據數量:1
消費。數據數量:0
生產。數據數量:1
生產。數據數量:2
生產。數據數量:3
生產。數據數量:4
生產。數據數量:5
生產。數據數量:6
生產。數據數量:7
生產。數據數量:8
消費。數據數量:7
消費。數據數量:6
消費。數據數量:5
消費。數據數量:4
消費。數據數量:3
消費。數據數量:2
消費。數據數量:1
消費。數據數量:0
main:退出


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章