Java生产者消费者

wait与nofity关键字是Java多线程的关键,主要是用来线程之间的通信用的,如果你的Java程序中有两个线程——即生产者和消费者,那么生产者可以通知消费者,让消费者开始消耗数据,因为队列缓 冲区中有内容待消费(不为空)。相应的,消费者可以通知生产者可以开始生成更多的数据,因为当它消耗掉某些数据后缓冲区不再为满。
我们可以利用wait()来让一个线程在某些条件下暂停运行。例如,在生产者消费者模型中,生产者线程在缓冲区为满的时候,消费者在缓冲区为空的时 候,都应该暂停运行。如果某些线程在等待某些条件触发,那当那些条件为真时,你可以用 notify 和 notifyAll 来通知那些等待中的线程重新开始运行。不同之处在于,notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll 会通知所有等待中的线程。换言之,如果只有一个线程在等待一个信号灯,notify和notifyAll都会通知到这个线程。但如果多个线程在等待这个信 号灯,那么notify只会通知到其中一个,而其它线程并不会收到任何通知,而notifyAll会唤醒所有等待中的线程。
1. 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。

  1. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。

  2. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。

  3. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。

  4. 基于前文提及的理由,更倾向用 notifyAll(),而不是 notify()。

以下我给出一个简单的消费者生产的例子帮助理解wait与notify关键字:

class WareHouse {
    // 存放货物的仓库,用栈结构模拟
    Stack<Object> goods = new Stack<Object>();

    // 仓库最大的保存量
    public static int maxSize = 100;

    // 初始化容量
    WareHouse(int initSize) {
        if (initSize < 0)
            return;
        for (int i = 0; i < initSize; i++) {
            goods.add(new Object());
        }
    }
    // 获取仓库当前容量
    public int size() {
        return goods.size();
    }
    // 向仓库中添加货物
    public boolean addGood() {
        return goods.add(new Object());
    }
    // 移除仓库当中的货物
    public boolean removeGood() {
        if (goods.size() == 0)
            return false;
        goods.pop();
        return true;
    }

}
/**
 * 
 * @author micro
 * @description 消费者
 *
 */
class Consumer implements Runnable {
    // 共享变量wareHouse
    private WareHouse wareHouse;
    private int num;

    Consumer(WareHouse wareHouse) {
        this.wareHouse = wareHouse;
    }
    // 消费数量
    public void consum(int num) {
        this.num = num;
    }

    public void run() {
        // 对仓库进行加锁
        synchronized (wareHouse) {
            try {
                System.out.println("我是消费者");
                wareHouse.notify(); // 唤醒阻塞在wareHouse对象上的线程
                System.out.println("仓库当前保存量:" + wareHouse.size() + ",最大保存量为:" + wareHouse.maxSize);
                if ((wareHouse.size() - num) > 0) {
                    for (int i = 0;i < num;i ++) {
                        wareHouse.removeGood();
                    }
                    System.out.println("消费了 :" + num + ",库存还有:" + wareHouse.size());
                } else {
                    System.out.println("需要消费 :" + num + ",当前库存不足");
                }
                System.out.println("消费完毕");
                System.out.println("当前库存:" + wareHouse.size());
                wareHouse.wait(); // 当前线程放弃wareHouse对象上的锁,并进入阻塞状态
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

}
/**
 * 
 * @author micro
 * @description 生产者
 *
 */
class Producer implements Runnable {

    private WareHouse wareHouse;
    private int num;

    Producer(WareHouse wareHouse) {
        this.wareHouse = wareHouse;
    }
    // 生产的数量
    public void produce(int num) {
        this.num = num;
    }

    public void run() {
        // 对仓库进行加锁
        synchronized (wareHouse) {
            try {
                System.out.println("我是生产者");
                wareHouse.notify(); // 唤醒阻塞在wareHouse上的线程
                // 仓库生产后还小于最大量
                System.out.println("仓库当前保存量:" + wareHouse.size() + ",最大保存量为:" + wareHouse.maxSize);
                if (wareHouse.size() + num < wareHouse.maxSize) {
                    System.out.println("仓库可以生产: " + (wareHouse.maxSize - wareHouse.size() )+ ",仓库需要生产:" + num);
                    for (int i = 0; i < num; i++) {
                        wareHouse.addGood();
                    }
                    System.out.println("生产:" + num + ",仓库生产完毕");
                } else {
                    System.out.println("无法生产");
                }
                System.out.println("当前库存:" + wareHouse.size());
                wareHouse.wait(); // 当前线程放弃wareHouse对象上的锁,并进入阻塞状态
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

}

演示:

        // 共享变量 (加锁对象)
        WareHouse wareHouse = new WareHouse(100);

        // 消费者
        Consumer consumer1 = new Consumer(wareHouse);
        consumer1.consum(60);

        // 生产者
        Producer producer1 = new Producer(wareHouse);
        producer1.produce(40);

        Producer producer2 = new Producer(wareHouse);
        producer2.produce(10);

        // 初始化三个线程
        Thread t1 = new Thread(consumer1);
        Thread t2 = new Thread(producer1);

        Thread t3 = new Thread(producer2);

        // 启动线程
        t1.start();
        t2.start();
        t3.start();

        // 主线程等待生产消费结束
        try {
            Thread.currentThread().sleep(1000);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        // 此时最后一个消费者或者是生产者肯定是在wait状态阻塞
        synchronized (wareHouse) { // 如果没有此代码片段,主线程永远不会结束,因为最后t3线程肯定会被阻塞不会被唤醒
            // 唤醒阻塞在wareHouse对象上阻塞的线程
            wareHouse.notify();
        }
        // 主线程结束
        System.out.println("结束");

打印结果:

我是消费者
仓库当前保存量:100,最大保存量为:100
消费了 :60,库存还有:40
消费完毕
当前库存:40
我是生产者
仓库当前保存量:40,最大保存量为:100
仓库可以生产: 60,仓库需要生产:10
生产:10,仓库生产完毕
当前库存:50
我是生产者
仓库当前保存量:50,最大保存量为:100
仓库可以生产: 50,仓库需要生产:40
生产:40,仓库生产完毕
当前库存:90
结束

下面演示一个关于银行取钱的笔试题

/**
 * 假设某个银行,它可以接受顾客的汇款,每做一次汇款,便可以计算出汇款的总额。现在有两个顾客对同一个银行账号操作,一个顾客分3次,每次将100元钱存入,
 * 每次存入后将钱的总额输出;另外一个顾客也分3次,每次取出100元钱,每次取出后将剩余的钱的总额输出。编写一个程序模拟实际的操作过程。
 * @author micro_hz
 *
 */
public class BankThreadTest {
    public static void main(String[] args) {
        final Bank bank = new Bank();
        new Thread(new Runnable (){

            @Override
            public void run() {
                for (int i = 0;i < 3;i ++) {
                    bank.addMoney(100);
                }
            }

        },"顾客A").start();

        new Thread(new Runnable (){

            @Override
            public void run() {
                for (int i = 0;i < 3;i ++) {
                    bank.carrayMoney(100);
                }
            }

        },"顾客B").start();

    }
}
class Bank implements Runnable {
    private Integer account = 0;
    @Override
    public void run() {
    }

    public synchronized void addMoney(Integer money) {
        account += money;
        System.out.println(Thread.currentThread().getName() + "存入 : " + money + ",剩余 : " + account);
        notify(); // 可能有人等着取钱,唤醒该线程
    }

    public synchronized void carrayMoney(Integer money) {
        if (account - money < 0) {
            // 如果钱小于0,只能存钱
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        account -= money;
        System.out.println(Thread.currentThread().getName() + "取出 :" + money + ",剩余 : " + account);
    }

}

用阻塞队列进行模拟生产消费者:

public static void main(String[] args) {
        WareHouse wareHouse = new WareHouse();
        Producer p1 = new Producer(wareHouse);
        p1.size = 100;
        new Thread(p1).start();
        Consumer c1 = new Consumer(wareHouse);
        c1.comsume(100);
        new Thread(c1).start();
    }


    static class WareHouse {
        // 用阻塞队列完成生产消费
        private LinkedBlockingQueue<Object> linkedBlockingQueue = new LinkedBlockingQueue<Object>();
        public void consume() {
            try {
                System.out.println("消费了---" + linkedBlockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void add(Object o) {
            System.out.println("生产了---" + linkedBlockingQueue.add(o));
        }
    }

    static abstract class AbstractOwner {
        protected WareHouse wareHouse;
        public AbstractOwner(WareHouse wareHouse) {
            this.wareHouse = wareHouse;
        }
    }

    static class Producer extends AbstractOwner implements Runnable {
        private int size;
        public Producer(WareHouse wareHouse) {
            super(wareHouse);
        }

        public void produce(int size) {
            this.size = size;
        }

        @Override
        public void run() {
            for (int i = 0;i < size;i ++) {
                // 这里让消费者更多占用cpu
                Thread.currentThread().yield();
                this.wareHouse.add(new Object());
            }
        }

    }

    static class Consumer extends AbstractOwner implements Runnable {
        private int size;
        public Consumer(WareHouse wareHouse) {
            super(wareHouse);
        }

        public void comsume(int size) {
            this.size = size;
        }

        @Override
        public void run() {
            for (int i = 0;i < size;i ++) {
                this.wareHouse.consume();
            }
        }

    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值