生產(chǎn)者消費(fèi)者

2019-07-07 23:45 更新

一、信號(hào)燈法 1.不使用信號(hào)燈法,僅使用synchronize解決生產(chǎn)者消費(fèi)者問題的錯(cuò)誤范例:

/**
 * 如果不使用信號(hào)燈法,可能出現(xiàn)以下幾種情況:
 * 1.消費(fèi)者可能在電影還沒有賦值就開始觀看,看到了null。
 * 2.消費(fèi)者可能看了一遍電影之后又看了同一部電影,甚至連續(xù)好多次。
 * 3.生產(chǎn)者可能在沒有人觀看這部電影時(shí)就更換的電影,即重新賦值。
 * 總結(jié):因?yàn)閟ynchronize只是增加了一個(gè)限制,即只能有一個(gè)線程訪問,并不具有其他邏輯,
 * 也就是說不會(huì)去判斷資源的多少、資源會(huì)不會(huì)總被一個(gè)線程使用,若要實(shí)現(xiàn)其他邏輯,就需要其他代碼輔助。
 */
public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        //共同的資源:
        Movie m = new Movie();

        
        //多線程:
        Player p = new Player(m);
        Watcher w = new Watcher(m);

        
        new Thread(p).start();
        new Thread(w).start();
    }
}
/**
 * 一個(gè)場(chǎng)景,共同的資源。
 */
class Movie{
    private String name;
    public synchronized void play(String name) {//播放
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        this.name = name;
        System.out.println("我要播放一部《" + name + "》電影。");
    } 
    public synchronized void watch() {//觀看
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("我看了一部《" + name + "》電影。");
    }
}
/**
 *生產(chǎn)者:播放者。
 */
class Player implements Runnable{
    private Movie m;
    public Player(Movie m) {
        this.m = m;
    }
    @Override
    public void run() {
        for(int i = 0; i < 20; i++) {
            if(i%2 == 0) {
                m.play("左青龍");
            } else {
                m.play("右白虎");
            }
        }
    }
}
/**
 *消費(fèi)者:觀眾。
 */
class Watcher implements Runnable{
    private Movie m;
    public Watcher(Movie m) {
        this.m = m;
    }
    @Override
    public void run() {
        for(int i = 0; i < 20; i++) {
            m.watch();
        }
    }
}

2.使用信號(hào)燈法,配合使用synchronize解決生產(chǎn)者消費(fèi)者問題的正確范例:

public class MyThread{
    public static void main(String[] args) throws InterruptedException {
        //共同的資源:
        Movie m = new Movie();

        
        //多線程:
        Player p = new Player(m);
        Watcher w = new Watcher(m);

        
        new Thread(p).start();
        new Thread(w).start();
    }
}
/**
 * 一個(gè)場(chǎng)景,共同的資源。
 */
class Movie{
    private String name;
    //信號(hào)燈
    //flag -->T 生產(chǎn)者生產(chǎn),消費(fèi)者等待,生產(chǎn)完成后通知消費(fèi)。
    //flag -->F 消費(fèi)者消費(fèi),生產(chǎn)者等待,消費(fèi)完成后通知生產(chǎn)。
    //wait()等待,釋放鎖,sleep不釋放鎖。
    //notify() 喚醒在此鎖等待隊(duì)列的最前面的一個(gè)線程取得鎖。
    //notifyAll() 喚醒在此鎖等待隊(duì)列的所有線程爭(zhēng)奪鎖。
    private boolean flag = true;

    
    public synchronized void play(String name) {//播放
        if(!flag) {//生產(chǎn)者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //開始生產(chǎn)
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        this.name = name;
        System.out.println("我要播放一部《" + name + "》電影。");
        //生產(chǎn)完畢
        //更改信號(hào)燈
        this.flag = false;
        //通知消費(fèi)
        this.notify();
    } 
    public synchronized void watch() {//觀看
        if(flag) {//消費(fèi)者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //開始消費(fèi)
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("我看了一部《" + name + "》電影。");
        //消費(fèi)完畢
        //更改信號(hào)燈,消費(fèi)停止
        this.flag = true;
        //通知生產(chǎn)
        this.notifyAll();
    }
}
/**
 *生產(chǎn)者:播放者。
 */
class Player implements Runnable{
    private Movie m;
    public Player(Movie m) {
        this.m = m;
    }
    @Override
    public void run() {
        for(int i = 0; i < 20; i++) {
            if(i%2 == 0) {
                m.play("左青龍");
            } else {
                m.play("右白虎");
            }
        }
    }
}
/**
 *消費(fèi)者:觀眾。
 */
class Watcher implements Runnable{
    private Movie m;
    public Watcher(Movie m) {
        this.m = m;
    }
    @Override
    public void run() {
        for(int i = 0; i < 20; i++) {
            m.watch();
        }
    }
}

運(yùn)行結(jié)果:

我要播放一部《左青龍》電影。
我看了一部《左青龍》電影。
我要播放一部《右白虎》電影。
我看了一部《右白虎》電影。
我要播放一部《左青龍》電影。
我看了一部《左青龍》電影。
我要播放一部《右白虎》電影。
我看了一部《右白虎》電影。
......

二、管盛法 暫不研究。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)