java多線程編程題(1)生產者消費者模式

前言

最近也看完了<<併發編程的藝術>>,也零零散散的看了不少多線程有關的東西。早上寫代碼的時候看到一篇博客講了使用notify和wait相關的一些東西。本人自己對多線程一直有點苦手,書本看了一大堆。但是實際使用依然頭大。這次突然對於多線程編碼有點感悟,記錄於此。

概念相關

synchronized{}鎖住的代碼塊,結束代碼執行之後。會釋放對應的鎖
wait和notify,可以在synchronized{}之中使用。即使拿到了鎖,也會在代碼塊中釋放出去。

題目1

現在有3個生產者,3個消費者,生產者每次+1。消費者每次-1。庫存從0開始,不能小於0。也不能大於5。要線程安全的進行生產和消費。
解答:多線程的編程處理的核心問題就在於變量。
一般有如下2個原則:
1、共享變量處理,使用static共享,或者傳入同一個類控制
2、每個線程控制自己的私有變量,ThreadLocal中的實現

代碼實現

這是一個庫存類

import java.util.ArrayList;
import java.util.List;

public class Storage {
    private List<Object> foods;

    public final static int MAX_SIZE = 5;

    private boolean flag = false;

    public Storage(){
        foods = new ArrayList<Object>();
    }
    public List<Object> getFoods() {
        return foods;
    }

    public void setFoods(List<Object> foods) {
        this.foods = foods;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

線程類

package main.java;

public class Company3 {
    public static void main(String[] args) {
        Company3 c = new Company3();
        Storage storage = new Storage();
        new Thread(c.new Customer(storage)).start();
        new Thread(c.new Producer(storage)).start();
        new Thread(c.new Customer(storage)).start();
        new Thread(c.new Producer(storage)).start();
        new Thread(c.new Customer(storage)).start();
        new Thread(c.new Producer(storage)).start();
    }

    /**
     * 消費者
     */
    private class Customer implements Runnable {
        private Storage storage;

        public Customer(Storage storage) {
            super();
            this.storage = storage;
        }

        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                synchronized (storage) {
                    System.out.println("消費者" + Thread.currentThread().getName() + "獲得鎖");
                    while (storage.getFoods().size() <= 0) {
                        System.out.println("貨物已空,提示生產者生產");
                        try {
                            System.out.println("消費者" + Thread.currentThread().getName() + "開始等待");
                            storage.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    storage.getFoods().remove(0);
                    System.out.println("消費者消費1, " + Thread.currentThread().getName() + ", 餘量:" + storage.getFoods().size());
                    storage.notifyAll();
                }
            }
        }
    }

    /**
     * 生產者
     */
    private class Producer implements Runnable {
        private Storage storage;

        public Producer(Storage storage) {
            super();
            this.storage = storage;
        }

        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                synchronized (storage) {
                    System.out.println("生產者" + Thread.currentThread().getName() + "獲得鎖");
                    while (storage.getFoods().size() >= Storage.MAX_SIZE) {
                        System.out.println("貨物已滿,提示消費者消費");
                        try {
                            System.out.println("生產者" + Thread.currentThread().getName() + "開始等待");
                            storage.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    storage.getFoods().add(1);
                    System.out.println("生產者生產1, " + Thread.currentThread().getName() + ",餘量:" + storage.getFoods().size());
                    storage.notifyAll();
                }
            }
        }
    }
}

效果如下

消費者消費1, Thread-0, 餘量:2
消費者Thread-2獲得鎖
消費者消費1, Thread-2, 餘量:1
消費者Thread-4獲得鎖
消費者消費1, Thread-4, 餘量:0
生產者Thread-3獲得鎖
生產者生產1, Thread-3,餘量:1
生產者Thread-1獲得鎖
生產者生產1, Thread-1,餘量:2
生產者Thread-5獲得鎖
生產者生產1, Thread-5,餘量:3
消費者Thread-0獲得鎖
消費者消費1, Thread-0, 餘量:2
消費者Thread-2獲得鎖
消費者消費1, Thread-2, 餘量:1
消費者Thread-4獲得鎖
消費者消費1, Thread-4, 餘量:0
消費者Thread-0獲得鎖
貨物已空,提示生產者生產

講解。
一個生產者類,一個消費者類。通過共享storage獲得一個顯式的變量進行控制,每次只能允許一個線程獲得storage的鎖。對其中的參數進行操作。當庫存爲0時,消費者wait停下當前線程,並且釋放鎖。循環等待庫存增多,其他線程進行爭搶鎖進行消費,同時喚醒其他線程,同理對於庫存5時,生產者也是一樣
tips:
1、我的線程在啓動後進行循環的頭位置,加了一個sleep。是因爲不加sleep的話,會導致偏向鎖很嚴重,一直會是該線程持有鎖,所以用sleep讓其他線程也可以爭搶到鎖。展示效果更佳明顯
2、如果添加更多的消費者或者生產者,那麼如果是更多消費者,會看到當消費到庫存爲0的時候,會有更多的消費者線程爭搶到線程,但是獲得不到鎖,就會wait等待中

題目2

我希望修改消費者和生產者的模式,當庫存爲0時,所有消費者停止消費,等待生產者生產,當生產者生產到5時,所有生產者也會停止,等待消費者消費

代碼2

public class Company {
    public static void main(String[] args) {
        Company c = new Company();
        Storage storage = new Storage();
        new Thread(c.new Customer(storage)).start();
        new Thread(c.new Producer(storage)).start();
        new Thread(c.new Customer(storage)).start();
        new Thread(c.new Producer(storage)).start();

    }

    /**
     * 消費者
     *
     * @author xtuali
     */
    private class Customer implements Runnable {
        private Storage storage;


        public Customer(Storage storage) {
            super();
            this.storage = storage;
        }

        public void run() {
            while (true) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                synchronized (storage) {
                    System.out.println("消費者" + Thread.currentThread().getName() + "獲得鎖" );
                    if (storage.isFlag()) {
                        while (storage.getFoods().size() <= 0) {
                            System.out.println("貨物已空,提示生產者生產");
                            storage.setFlag(false);
                            try {
                                storage.notifyAll();
                                System.out.println("消費者" + Thread.currentThread().getName() + "開始等待" );
                                storage.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        storage.getFoods().remove(0);
                        System.out.println("消費者消費1, " + Thread.currentThread().getName() + ", 餘量:" + storage.getFoods().size());
                    }else{
                        try {
                            System.out.println("消費者" + Thread.currentThread().getName() + "開始等待" );
                            storage.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 生產者
     *
     * @author xtuali
     */
    private class Producer implements Runnable {
        private Storage storage;


        public Producer(Storage storage) {
            super();
            this.storage = storage;
        }

        public void run() {
            while (true) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                synchronized (storage) {
                    System.out.println("生產者" + Thread.currentThread().getName() + "獲得鎖" );
                    if(!storage.isFlag()){
                        while (storage.getFoods().size() >= Storage.MAX_SIZE) {
                            System.out.println("貨物已滿,提示消費者消費");
                            storage.setFlag(true);
                            try {
                                storage.notifyAll();
                                System.out.println("生產者" + Thread.currentThread().getName() + "開始等待" );
                                storage.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        storage.getFoods().add(1);
                        System.out.println("生產者生產1, " + Thread.currentThread().getName() + ",餘量:" + storage.getFoods().size());
                    }else{
                        try {
                            System.out.println("生產者" + Thread.currentThread().getName() + "開始等待" );
                            storage.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

結果

消費者Thread-0獲得鎖
消費者Thread-0開始等待
生產者Thread-1獲得鎖
生產者生產1, Thread-1,餘量:1
消費者Thread-2獲得鎖
消費者Thread-2開始等待
生產者Thread-3獲得鎖
生產者生產1, Thread-3,餘量:2
生產者Thread-1獲得鎖
生產者生產1, Thread-1,餘量:3
生產者Thread-3獲得鎖
生產者生產1, Thread-3,餘量:4
生產者Thread-1獲得鎖
生產者生產1, Thread-1,餘量:5
生產者Thread-3獲得鎖
貨物已滿,提示消費者消費
生產者Thread-3開始等待
消費者Thread-0獲得鎖
消費者消費1, Thread-0, 餘量:4
消費者Thread-2獲得鎖
消費者消費1, Thread-2, 餘量:3

解答:
我在Storage類中已經添加了flag標記,flag初始標記設置爲false,因爲開始的時候沒有庫存,一切都要等待生產者生產。flag也用於控制消費者在生產者生產的時候放棄消費並等待,也用於控制生產者在消費者消費的時候放棄生產,等待消費。

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