java實現生產者消費者模式

生產者消費者問題是一個著名的線程同步問題,該問題描述如下:有一個生產者在生產產品,這些產品將提供給若干個消費者去消費,爲了使生產者和消費者能併發執行,在兩者之間設置一個具有多個緩衝區的緩衝池,生產者將它生產的產品放入一個緩衝區中,消費者可以從緩衝區中取走產品進行消費,顯然生產者和消費者之間必須保持同步,即不允許消費者到一個空的緩衝區中取產品,也不允許生產者向一個已經放入產品的緩衝區中再次投放產品。

使用synchronized關鍵字實現線程同步

在使用wait()和notifyAll()方法時,應注意將wait()方法放入循環中,否則會產生虛假喚醒問題。

/**
 * Created by 吳海飛 on 2017-1-23.
 */
public class TestProductAndConsumer {
    public static void main(String[] args){
        Clerk clerk = new Clerk();
        Productor pro = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);
        new Thread(pro,"生產者A").start();
        new Thread(consumer,"消費者B").start();
    }
}

/**
 * 店員,可以進貨與銷售貨物
 */
class Clerk{

    private int product = 0;

    /**
     * 進貨的方法
     */
    public synchronized void get(){
        while (product>=1){//爲了避免虛假喚醒問題,應該總是使用在循環中
            System.out.println("產品已滿!");

            try {
                this.wait();//等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + ":" + ++product);
        this.notifyAll();//喚醒線程
    }

    /**
     * 銷售的方法
     */
    public synchronized void sale(){
        while (product<=0){//爲避免虛假喚醒,應該總是始終使用在循環中
            System.out.println("缺貨……");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + ":"+ --product);
        this.notifyAll();
    }
}

/**
 * 生產者
 */
class Productor implements Runnable{

    private Clerk clerk;

    public Productor(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.get();
        }
    }
}

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

    private Clerk clerk;

    public Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            clerk.sale();
        }
    }
}

使用同步鎖實現線程同步問題

使用同步鎖時應注意lock()與unlock()方法的同步使用。


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用ReentrantLock實現生產者消費者問題
 * Created by 吳海飛 on 2017-1-23.
 */
public class TestReentrantLock {
    public static void main(String[] args){
        Clerk clerk = new Clerk();
        Productor pro = new Productor(clerk);
        Consumer consumer = new Consumer(clerk);

        new Thread(pro,"生產者A").start();
        new Thread(consumer,"消費者B").start();
        new Thread(pro,"生產者C").start();
        new Thread(consumer,"消費者D").start();
    }
}

class Clerk{
    private Lock lock = new ReentrantLock();//獲取同步鎖
    private Condition condition = lock.newCondition();
    private int product = 0;


/**
 * 進貨的方法
 */

    public void get(){
        lock.lock();//打開鎖
        try{
            while (product>=1){//爲了避免虛假喚醒問題,應該總是使用在循環中
                System.out.println("產品已滿!");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ":" + ++product);
            condition.signalAll();
        }finally {
            lock.unlock();//關閉鎖
        }

    }


/**
 * 銷售的方法
 */

    public void sale(){
        lock.lock();//加鎖
        try {
            while (product<=0){//爲避免虛假喚醒,應該總是始終使用在循環中
                System.out.println("缺貨……");
                try {
                    condition.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ":"+ --product);
            condition.signalAll();//喚醒等待
        }finally {
            lock.unlock();//釋放鎖
        }

    }
}


/**
 * 生產者
 */

class Productor implements Runnable{

    private Clerk clerk;

    public Productor(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.get();
        }
    }
}


/**
 * 消費者
 */

class Consumer implements Runnable{

    private Clerk clerk;

    public Consumer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            clerk.sale();
        }
    }
}
發佈了46 篇原創文章 · 獲贊 14 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章