1. 程式人生 > >菜雞的Java筆記 生產者與消費者

菜雞的Java筆記 生產者與消費者

生產者與消費者
        程式碼要求知道做什麼用即可
        執行緒間的通訊問題以及 Object 類的支援
        
    基礎模型
        現在希望實現一種資料的生產和取出的操作形式,即:有兩個甚至更多的執行緒物件,這樣的執行緒分為生產者執行緒和消費者執行緒
        那麼最理想的狀態是生產者每生產完一條完整的資料之後,消費者就要取走這個資料,並且進行輸出的列印
        現在假設要輸出的資訊有這樣兩個:
            title = 帥帥, content = 一個學生;
            title = 可愛的小動物, content = 小貓咪;
            
        範例:程式的初期實現

package cn.mysterious.actualcombat;
class Info{
    private String title;
    private String content;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    
public void setContent(String content) { this.content = content; } } class Producptor implements Runnable{ private Info info = null; public Producptor(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for
(int i = 0; i < 50; i++) { if (i % 2 == 0) { // 偶數 this.info.setTitle("帥帥"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.info.setContent("一個學生"); }else { this.info.setTitle("可愛的動物"); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.info.setContent("小貓咪"); } } } } class Consumer implements Runnable{ private Info info = null; public Consumer(Info info){ this.info = info; } @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(this.info.getTitle() + "-->" + this.info.getContent()); } } } public class ActualCombat{ public static void main(String[] args){ Info info = new Info(); Producptor p = new Producptor(info); Consumer c = new Consumer(info); new Thread(p).start(); new Thread(c).start(); } }

           
        通過以上的執行可以發現有兩個問題:
            第一:資料錯位了
            第二:重複生產,重複取出
            
    解決資料不同步問題
        要想解決同步問題一定使用同步程式碼塊或者是同步方法,既然要同步,那麼肯定要將設定屬性和取得的屬性的內容都統一交給 Info 完成
        
        範例:

package cn.mysterious.actualcombat;
class Info{
    private String title;
    private String content;
    public synchronized void set(String content, String title){
        this.title = title;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        this.content = content;
    }
    public synchronized void get(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.title + "-->" +this.content );
    }
}
class Producptor implements Runnable{
    private Info info = null;
    public Producptor(Info info){
        this.info = info;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 50; i++) {
            if (i % 2 == 0) { // 偶數
                this.info.set("帥帥","一個學生");
            }else {
                this.info.set("可愛的動物","小貓咪");
            }
        }
    }
    
}
class Consumer implements Runnable{
    private Info info = null;
    public Consumer(Info info){
        this.info = info;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 50; i++) {
            this.info.get();
        }
    }
}
public class ActualCombat{
    public static void main(String[] args){
        Info info = new Info();
        Producptor p = new Producptor(info);
        Consumer c = new Consumer(info);
        new Thread(p).start();
        new Thread(c).start();

    }
}

           
            所有的設定和取得資料的操作都交給了同步方法完成
            現在同步問題解決了,但是重複問題更嚴重了
            
    解決重複操作
        如果要想解決重複問題,那麼必須加入等待與喚醒的處理機制,而這樣的操作方法是有 Object 類所提供的
        在 Object 類中提供有如下幾種方法:
            等待: public final void wait() throws InterruptedException{}
            喚醒第一個等待執行緒: public final void notify();
            喚醒全部等待執行緒: public final void notifyAll();
            
        範例:修改Info類

package cn.mysterious.actualcombat;
class Info{
    private String title;
    private String content;
    private boolean flag = true;
    // flag = true 表示可以生產,但是不允許取走資料
    // flag = false 表示可以取走資料,但是不允許生產資料
    public synchronized void set(String title, String content){
        if (this.flag = false) { // 表示已經生產過了,還未取走
            try {
                super.wait(); // 等待
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } // 沒有生產,可以生產
        this.title = title;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        this.content = content;
        this.flag = false;// 表示生產過了
        super.notify();
    }
    public synchronized void get(){
        if (this.flag = true) { // 此時應該生產,不應該取走資料
            try {
                super.wait(); // 等待
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(this.title + "-->" +this.content );
        this.flag = true; // 表示取走了
        super.notify();
    }
}
class Producptor implements Runnable{
    private Info info = null;
    public Producptor(Info info){
        this.info = info;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 50; i++) {
            if (i % 2 == 0) { // 偶數
                this.info.set("帥帥","一個學生");
            }else {
                this.info.set("可愛的動物","小貓咪");
            }
        }
    }
    
}
class Consumer implements Runnable{
    private Info info = null;
    public Consumer(Info info){
        this.info = info;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 50; i++) {
            this.info.get();
        }
    }
}
public class ActualCombat{
    public static void main(String[] args){
        Info info = new Info();
        Producptor p = new Producptor(info);
        Consumer c = new Consumer(info);
        new Thread(p).start();
        new Thread(c).start();

    }
}

           
        面試題:請解釋 sleep() 與 wait() 的區別?
            sleep() 是 Thread 類定義的方法,在休眠一定時間之後自己喚醒
            wait() 是 Object 類定義的方法,表示執行緒要等待執行,必須通過 notify(),notifyAll() 方法來進行喚醒

    總結
        生產者和消費者這是一個模型,完整的體現了執行緒的同步, Object 類的支援