天天看點

JAVA多線程——線程協作(生産者消費者問題)JAVA多線程——線程協作

JAVA多線程——線程協作

生産者消費者問題

生産者消費者問題:這是一個線程同步問題,生産者消費者共享同一個資源,并且生産者消費者之間互相依賴,互為條件。

對于生産者,沒有生産産品之前,要通知消費者等待,而生産了産品之後,又要放到緩存區,通知消費者來消費

對于消費者,在消費之後,要通知生産者已經結束消費,需要生産新的産品以供消費

在生産者消費者問題中,僅有的synchronized是不夠的

synchronized實作了同步,但不能用來實作不同線程之間的通信

解決線程之間通信問題的方法

wait() 讓線程等待,直到其他線程通知,與sleep不同,會釋放鎖

wait(long timeout) 指定等待的毫秒數

notify() 喚醒處于等待狀态的線程

notifyAll() 喚醒同一個對象上所有等待的線程,優先級别高的先排程

說明

這些方法都是定義在java.lang.Object類中的方法,都隻能在同步方法或同步塊中使用,方法的調用者必須是同步代碼塊或者同步方法中的同步螢幕,否則會出現異常IllegalMonitorStateException

補充:sleep()和wait()的異同

同:一旦執行方法,都可以使目前線程進入阻塞狀态

異:

  • 兩個方法聲明的位置不同,Thread類中聲明sleep(),Object類中聲明wait()
  • 結束阻塞狀态的不同,sleep()時間到了就會結束,wait()要通過notifyAll() 方法
  • 調用的要求不同,sleep()可以在任何場景下調用,wait()必須使用在同步代碼塊或者同步方法中
  • 如果兩個方法否使用在同步代碼塊或者同步方法中,sleep()不會釋放鎖,wait()會釋放鎖

小結

​ 釋放鎖的操作:

  • 目前線程的同步代碼塊、同步方法結束
  • 目前線程在同步代碼塊、同步方法結中遇到break、return終止了該代碼塊,該方法的繼續執行
  • 目前線程在同步代碼塊、同步方法結中出現了未處理的Error或Execption,導緻異常結束
  • 使用了wait()方法

不會釋放鎖的操作:

  • 執行sleep(),和yield()方法
  • 線程執行同步代碼塊時,其他線程調用了該線程的suspend()方法将該線程挂起,該線程不會釋放鎖(盡量避免使用suspend()和resume()來控制線程)

解決方式

生産者消費者問題解決方式1:管程法

生産者:負責生産資料的子產品(可能是放啊,對象,線程,程序)

消費者:負責處理資料的子產品(可能是放啊,對象,線程,程序)

緩沖區:消費者不能直接使用生産者的資料,他們之間有個緩沖區

package com.peng.demon06;

//測試:用管程法解決生産者消費者問題,利用緩沖區

//需要的對象:生産者,消費者,産品,緩沖區
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        /*建立線程
        Productor productor = new Productor(container);
        Consumer consumer = new Consumer( new Consumer);
        開啟線程
        productor.start();
        consumer.start();
        可以簡化為下面兩行代碼
        */
        new Productor(container).start();
        new Consumer(container).start();

    }
}
//生産者
class Productor extends Thread{
    SynContainer container;
    //用構造器建立容器的對象
    public Productor(SynContainer container){
        this.container=container;
    }

    //生産的方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {

            container.push(new Chicken(i));//把雞放進容器
            System.out.println("生産了"+i+"隻雞");
            
        }
    }
}
//消費者
class Consumer extends Thread{
    SynContainer container;
    //用構造器建立容器的對象
    public Consumer(SynContainer container){
        this.container=container;
    }

    //消費的方法

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消費了-->"+container.pop().id+"隻雞");

        }
    }
}
//産品
class Chicken{
    
    int id;//定義産品的編号

    public Chicken(int id) {
        this.id = id;
    }
}
//緩沖區(容器)
class SynContainer{

    //容器的大小,可以放10個産品
    Chicken[] chickens = new Chicken[10];

    //計算容器裡産品的數量
    int count = 0;
    
    //生産者放入産品
    //synchronized同步方法,讓線程同步
    public synchronized void push(Chicken chicken){

        //如果容器滿了,就需要消費者消費,然後生産者等待
        while (count == 10){
            //生産者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //如果沒有滿,生産者丢入産品
        chickens[count]=chicken;//把chicken丢進chickens數組的計數器裡
        count++;

        //放完通知消費者來消費
        this.notifyAll();

    }
    //消費者消費産品
    public synchronized Chicken pop(){
        //判斷能否消費
       while (count==0){ //如果容器為空,
            //等待生産者生産産品,消費者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消費
        count--;
        Chicken chicken = chickens[count];//把chickens數組的計數器裡的産品取出來

        //消費完了,通知生産者生産,再放到緩沖區
        this.notifyAll();
        return chicken;
        
    }
}
           

JAVA多線程——線程協作(生産者消費者問題)JAVA多線程——線程協作

生産者消費者問題解決方式2:信号燈法

通過标志位來解決

package com.peng.demon06;
//測試信号燈法——通過标志位
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生産者-->演員
class Player extends Thread{
    TV tv = new TV();
    public Player(TV tv){
        this.tv = tv;
    }
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                if (i%2==0){
                    tv.play("中餐廳");
                }else {
                    tv.play("廣告");
                }

            }
        }
}

//消費者-->觀衆
class Watcher extends Thread {
    TV tv = new TV();
    public Watcher(TV tv) {
       this.tv = tv;
    }

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

//産品-->節目(不需要緩沖區了)
class TV{
    //演員表演,觀衆等待    true
    //觀衆觀看,演員等待    false
    String voice;//表演的節目
    boolean flag = true;//标志位

    //表演
    public synchronized void play(String voice) {
        while (flag == false) {//如果标志位為false
            try {
                this.wait();//讓演員等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

            //通知觀衆觀看
            this.notifyAll();//喚醒觀衆
            this.voice = voice;
            System.out.println("演員表演了" + voice);
            this.flag = !this.flag;//取反
        }

    //觀看
    public synchronized void watch(){
        while (flag==true) {//如果标志位為true
            try {
                this.wait();//讓觀衆等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

            //通知演員表演,喚醒等待
            this.notifyAll();
            System.out.println("觀衆觀看了:" + voice);
            this.flag = !this.flag;//取反

        }
}
           
JAVA多線程——線程協作(生産者消費者問題)JAVA多線程——線程協作