多線程之生產者與消費者模型

在這個模型中我們需要有三個主要類,分別是商品類,生產者類以及消費者類。
其中運行機制和我們平時的生產者與消費者的關係機制大同小異。
在這個模型中,我們需要定義一個容器來實現消費者與生產者之間的解耦,爲了更貼近實際,採用FIFO原則的隊列作爲容器爲最佳選擇,並且此模型還遵循着以下兩條規則:
1.當生產者生產商品數量超過容器限制時,停止生產;
2.當消費者消費商品導致容器爲空時,停止消費。
以下通過兩種方式來實現這個模型。
第一種方式:wait()方法和notify()方法
注意:在這個方法中需要把容器作爲對象賦鎖。
首先定義一個商品類:

class Goods {
    private String name;
    private String id;
    private Double price;

    public Goods(String name, String id, Double price) {
        this.name = name;
        this.id = id;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                ", price=" + price +
                '}';
    }
}

定義一個生產者類:

class Producer implements Runnable {
    private final Queue<Goods> queue;

    public Producer(Queue<Goods> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this.queue) {
                if (queue.size() >= 3) {
                    System.out.println(Thread.currentThread().getName() + "容器已滿,停止生產");
                    try {
                        this.queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    String id = UUID.randomUUID().toString();
                    String name = "包子";
                    Double price = new Random().nextDouble();
                    Goods goods = new Goods(id, name, price);
                    queue.add(goods);
                    System.out.println(Thread.currentThread().getName() + "生產了一個" + goods);
                }
            }
        }
    }
}

定義一個消費者類:

class Consumer implements Runnable {
    private Queue<Goods> queue;

    public Consumer(Queue<Goods> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this.queue) {
                if (this.queue.isEmpty()) {
                    System.out.println(Thread.currentThread().getName() + "容器已空,開始生產");
                    this.queue.notifyAll();
                } else {
                    Goods goods = this.queue.poll();
                    System.out.println(Thread.currentThread().getName() + "消費了一個" + goods);
                }
            }
        }
    }
}

最後定義一個測試用例:

public static void code1() {
        //生產者
        //消費者
        //容器
        Queue<Goods> queue = new LinkedList<>();
        Runnable produce = new Producer(queue);
        Runnable consumer = new Consumer(queue);

        //生產者線程
        for (int i = 0; i < 5; i++) {
            new Thread(produce, "生產者-" + i).start();
        }

        //消費者線程
        for (int i = 0; i < 2; i++) {
            new Thread(consumer, "消費者-" + i).start();
        }
    }

方法二:使用阻塞隊列(這個方法好像還有一點問題,回頭再重新修改)
由於BlockingQueue接口自帶阻塞的功能,所以也不需要格外加鎖。
如果該隊列已滿,該線程被阻塞;如果該隊列已空,該線程也被阻塞。
首先定義一個商品類:

class Goods1 {
    private String name;
    private String id;
    private Double price;

    public Goods1(String name, String id, Double price) {
        this.name = name;
        this.id = id;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Goods{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                ", price=" + price +
                '}';
    }
}

定義一個生產者類:

class Producer1 implements Runnable {
    private BlockingQueue<Goods1> queue;

    public Producer1(BlockingQueue<Goods1> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        String id = UUID.randomUUID().toString();
        String name = "包子";
        Double price = new Random().nextDouble();
        Goods1 goods = new Goods1(id, name, price);
        while (true) {
            System.out.println(Thread.currentThread().getName() + "準備生產!");
            try {
                Thread.sleep(500);
                queue.put(goods);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "生產完成:" + goods);
        }
    }
}

定義一個消費者類:

class Consumer1 implements Runnable {
    private BlockingQueue<Goods1> queue;

    public Consumer1(BlockingQueue<Goods1> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + "準備消費!");
            try {
                Thread.sleep(500);
                Goods1 goods = queue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "消費完成:" + goods);
        }
    }
}

最後定義一個測試用例:

public static void main(String[] args) {
        BlockingQueue<Goods1> queue = new ArrayBlockingQueue<>(3);
        Runnable produce = new Producer1(queue);
        Runnable consumer = new Consumer1(queue);
        //生產者線程
        for (int i = 0; i < 3; i++) {
            new Thread(produce, "生產者-" + i).start();
        }


        //消費者線程
        for (int i = 0; i < 2; i++) {
            new Thread(consumer, "消費者-" + i).start();
        }
    }

以上就是實現生產者與消費者模型的兩種方式。

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