java多线程 一个生产者和多个消费者

生产者和消费者模式是面试时很容易被问到的一类题,在平常的开发中也经常碰到,比如在网游开发中:用一个线程把收到的字节数据封装起来写到一个队列中,然后用一个或多个线程从该队列中把数据读取出来再分发。本文来实现类似的情景: 5个通宵加班并饿了一天的程序员去包子店吃小笼包,小笼包分为菜包和肉包,随机分配的,由于公司只给报销20个包子的钱,所以就只点了20个包子,包子店的老板娘一次只能蒸10个包子,而且是必须等他们吃完了蒸好的10个包子之后再继续做剩下的10个包子,由于这个世界本身是不公平的,所以抢的快的的就多吃点,但为了避免他们把包子抢烂,规定每次只能一个人去拿包子
这里生产者就是做包子的,消费者就是这5个饿晕了的coder,下面来看看怎么用代码实现它。
1. 创建包子店BreadShop.java,首先创建一些公用的数据
// 装包子的盘子
private ArrayList breadList = new ArrayList();
// 包子的种类:肉包和菜包
private BreadType breadTypes[] = { BreadType.MEAT, BreadType.VEGETABLES };
// 已经出炉的包子总数
private int totalCount = 0;
// 点的包子数
private final int MAX_COUNT = 20;
 
enum BreadType {
    MEAT, VEGETABLES
}
 
// 包子
class Bread {
    public BreadType type;
 
    public Bread(BreadType type) {
        this.type = type;
    }
}
2. 首先来了解一下代码中用到的几个方法:wait()、sleep()、notify()、notifyAll()
wait()和notify()以及notifyAll()是Object的方法,这三个方法跟锁有密切关系,必须写在synchronized代码块中。obj.wait()表示线程会释放对象obj的锁,处于等待状态,直到有其他线程调用obj对象的notify()或者notifyAll()函数,才有机会重新去竞争obj对象的锁。notify()和notifyAll()的区别:前者是唤醒等待池中的一个线程,然后系统会让它获得obj对象的锁;后者是唤醒所有等待该对象的线程,然后让它们去竞争obj对象的锁。虽然使用notify()或者notifyAll()都可以,但大多数情况下还是notifyAll()安全些,notify()可能由于一些代码缺陷出现死锁,特别是在一个生产者和多个消费者的模式中。
sleep()是Thread的函数,它指定线程休眠一段时间,但是不会释放对象的锁。
下面来实现生产者
class Product implements Runnable {
    private boolean isWork = false;
 
    public Product()
    {
        this.isWork = true;
    }
 
    // 把包子蒸熟后放到盘子里
    public void makeBread(Bread bread) {
        breadList.add(bread);
        switch(bread.type)
        {
        case MEAT:
            System.out.println("make a meat bread");
            break;
 
        case VEGETABLES:
            System.out.println("make a vegetables bread");
            break;
        }
    }
 
    @Override
    public void run() {
        while(isWork)
        {
            try {
                synchronized(breadList)
                {
                    // 他们还没吃完,继续等待
                    if(breadList.size() > 0)
                        breadList.wait();
                    // 一次蒸10个包子
                    for(int i = 0; i < 10; ++ i)
                    {
                        int type = new Random().nextInt(2);
                        Bread bread = new Bread(breadTypes[type]);
                        this.makeBread(bread);
                    }
                    totalCount += 10;
                    // 通知他们可以吃包子了
                    breadList.notifyAll();
                }
                // 做完了20个包子
                if(totalCount >= MAX_COUNT)
                {
                    isWork = false;
                }
            }catch(Exception e) {
                e.printStackTrace();
                isWork = false;
            }
        }
 
    }
}

盘子里如果有包子,老板娘则等他们吃完;如果盘子里没有包子,则立即做10个包子,蒸熟后通知那5个coder来拿包子,直到做完20个包子。
3. 实现消费者
class Consumer implements Runnable
{
 
    private int id;
    public Consumer(int id)
    {
        this.id = id;
    }
    // 吃包子
    public void eat(Bread bread)
    {
        BreadType type = bread.type;
        switch(type)
        {
        case MEAT:
            System.out.println("AlexZhou " + id + " eat a meat bread");
            break;
 
        case VEGETABLES:
            System.out.println("AlexZhou " + id + " eat a vegetables bread");
            break;
        }
    }
    @Override
    public void run() {
        while(true)
        {
            try{
                synchronized(breadList)
                {
                    // 包子还没做好
                    if(breadList.size() == 0)
                    {
                        // 吃完了所有包子
                        if(totalCount >= MAX_COUNT)
                            break;
                        // 通知老板娘赶快做包子
                        breadList.notifyAll();
                        // 等老板娘做包子
                        breadList.wait();
                    }
                    else
                    {
                        // 从盘子里拿包子吃
                        Bread bread = breadList.remove(0);
                        this.eat(bread);
 
                    }
                }
                // 这里模拟吃包子的时间,也可以增大其他线程获得锁的概率,提高公平性
                Thread.sleep(100);
 
            }catch(Exception e)
            {
                e.printStackTrace();
                break;
            }
        }
 
    }
}

盘子里如果有包子就去拿包子,拿到后就跑到角落里去吃,吃完后继续去拿;盘子里如果没有包子就叫老板娘继续做包子,直到吃完20个。
4. 现在来模拟这个情景
public static void main(String[] args) {
    BreadShop bs = new BreadShop();
    // 5个coder来到包子店点包子
    for(int i = 0; i < 5; ++ i)
    {
        Thread t = new Thread(bs.new Consumer(i));
        t.start();
    }
    // 老板娘开始做包子 
    Thread productThread = new Thread(bs.new Product());
    productThread.setPriority(Thread.MAX_PRIORITY);
    productThread.start();
}

打印的结果如下:
make a meat bread
make a vegetables bread
make a vegetables bread
make a vegetables bread
make a meat bread
make a vegetables bread
make a meat bread
make a meat bread
make a meat bread
make a vegetables bread
AlexZhou 1 eat a meat bread
AlexZhou 3 eat a vegetables bread
AlexZhou 2 eat a vegetables bread
AlexZhou 0 eat a vegetables bread
AlexZhou 4 eat a meat bread
AlexZhou 3 eat a vegetables bread
AlexZhou 1 eat a meat bread
AlexZhou 2 eat a meat bread
AlexZhou 0 eat a meat bread
AlexZhou 4 eat a vegetables bread
make a meat bread
make a meat bread
make a vegetables bread
make a meat bread
make a meat bread
make a vegetables bread
make a meat bread
make a meat bread
make a meat bread
make a meat bread
AlexZhou 1 eat a meat bread
AlexZhou 2 eat a meat bread
AlexZhou 4 eat a vegetables bread
AlexZhou 1 eat a meat bread
AlexZhou 2 eat a meat bread
AlexZhou 3 eat a vegetables bread
AlexZhou 0 eat a meat bread
AlexZhou 4 eat a meat bread
AlexZhou 2 eat a meat bread

AlexZhou 1 eat a meat bread


转载请注明来自:Alex Zhou的程序世界,本文链接:http://codingnow.cn/java/1053.html

你可能感兴趣的:(java语言)