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也用于控制消费者在生产者生产的时候放弃消费并等待,也用于控制生产者在消费者消费的时候放弃生产,等待消费。

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