• Java Concurrency


    生产者消费者问题是一个常见的多线程同步案例:一组生产者线程和一组消费者线程共享一个初始状态为空、大小为 N 的缓冲区。只有当缓冲区没满的时候,生产者才能把消息放入缓冲区,否则必须等待;只有缓冲区不空的时候,消费者才能从缓冲区取消息,否则必须等待。由于缓冲区是临界资源,在同一时间,它只允许一个生产者放入消息,或者一个消费者从中取出消息。

    生产者消费者问题涉及到了多个线程通信的等待通知机制,生产者线程和消费者只能满足一定条件下才能操作共享的缓冲区,否则只能挂起等待,直到被唤醒重新竞争共享资源。Java API 提供了几个方法来实现这一机制:wait、notify、notifyAll。

    wait: 在调用 wait 方法之前,线程必须获得该对象的对象级别锁。即只能在同步方法或同步块中调用 wait 方法,否则 JVM 会抛出 IllegalMonitorStateException 异常。在执行 wait 方法后,当前线程会释放锁。在 wait 方法返回之前,线程和其他线程重新竞争该对象锁。

    notify: 在调用 notify 方法之前,线程同样须要获得该对象的对象级别锁,否则 JVM 会抛出 IllegalMonitorStateException 异常。notify 方法通知那些呈 wait 态的其他线程,如果有多个线程等待,则选择其中一个线程准备获得该对象锁。notify 方法执行完毕,呈 wait 状态的线程并不会立即获得该对象锁,须等到执行 notify 方法的线程执行完同步方法或同步块后,对象锁才会被释放,呈 wait 状态的线程才能获得锁。

    notifyAll: notifyAll 与 notify 在于,notify 只唤醒一个等待锁的线程,而 notifyAll 会唤醒所有等待锁的线程。

    生产者消费者问题示例

    临界资源缓冲区

    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 仓库
     * @author  huey
     * @created 2016年10月19日
     * @updated 2016年10月19日
     * @version 1.0.0
     */
    public class Storehouse {
    
        private List<Object> buffer;
        private int maxSize;            // 仓库最多允许存储 maxSize 个物品
        
        public Storehouse(int maxSize) {
            if (maxSize <= 0 || maxSize > 100) {
                throw new IllegalArgumentException("maxSize 参数的值必须在 1 到 100之间");
            }
            this.maxSize = maxSize;
            buffer = new ArrayList<Object>();
        }
        
        public synchronized void add(Object data) {
            if (buffer.size() == maxSize) {
                throw new IndexOutOfBoundsException("仓库已经饱和,不能再输入物品。");
            }
            buffer.add(data);
            System.out.printf("输入物品,仓库当前有 %d 个物品。
    ", buffer.size());
        }
        
        public synchronized void remove() {
            buffer.remove(0);
            System.out.printf("输出物品,仓库当前有 %d 个物品。
    ", buffer.size());
        }
        
        public boolean isEmpty() {
            return buffer.isEmpty();
        }
        
        public boolean isFull() {
            return buffer.size() == maxSize;
        }
    }

    生产者

    import java.util.Random;
    
    /**
     * 生产者
     * @author  huey
     * @created 2016年10月19日
     * @updated 2016年10月19日
     * @version 1.0.0
     */
    public class Producer extends Thread {
        
        private Storehouse storehouse;
        
        public Producer(String name, Storehouse storehouse) {
            super(name);
            this.storehouse = storehouse;
        }
    
        /**
         * 生产者生产物品
         * @author  huey
         * @created 2016年10月19日
         * @updated 2016年10月19日
         */
        public void produce() {
            synchronized (storehouse) {
                while (storehouse.isFull()) {
                    try {
                        System.out.printf("仓库当前处于饱和状态,生产者  %s 须挂起等待。
    ", Thread.currentThread().getName());
                        storehouse.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                storehouse.add(new Object());
                storehouse.notifyAll();
            }
        }
        
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                this.produce();
                try {
                    Thread.sleep(new Random().nextInt(50));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    消费者

    import java.util.Random;
    
    /**
     * 消费者
     * @author  huey
     * @created 2016年10月19日
     * @updated 2016年10月19日
     * @version 1.0.0
     */
    public class Consumer extends Thread {
    
        private Storehouse storehouse;
        
        public Consumer(String name, Storehouse storehouse) {
            super(name);
            this.storehouse = storehouse;
        }
        
        /**
         * 消费者消费物品
         * @author  huey
         * @created 2016年10月18日
         * @updated 2016年10月18日
         */
        public void consume() {
            synchronized (storehouse) {
                while (storehouse.isEmpty()) {
                    try {
                        System.out.printf("仓库当前处于空虚状态,消费者 %s 须挂起等待。
    ", Thread.currentThread().getName());
                        storehouse.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                storehouse.remove();
                storehouse.notifyAll();
            }
        }
        
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                this.consume();
                try {
                    Thread.sleep(new Random().nextInt(100));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    测试用例

    /**
     * 
     * @author  huey
     * @created 2016年10月19日
     * @updated 2016年10月19日
     * @version 1.0.0
     */
    public class Main {
        public static void main(String[] args) {
            Storehouse storehouse = new Storehouse(5);
            
            Thread[] producers = new Thread[] {
                new Producer("P1", storehouse),
                new Producer("P2", storehouse)
            };
            for (Thread producer : producers) {
                producer.start();
            }
            
            Thread[] consumers = new Thread[] {
                new Consumer("C1", storehouse),
                new Consumer("C2", storehouse),
                new Consumer("C3", storehouse),
                new Consumer("C4", storehouse)
            };
            for (Thread consumer : consumers) {
                consumer.start();
            }
        }    
    }

    运行结果:

    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    仓库当前处于空虚状态,消费者 C2 须挂起等待。
    仓库当前处于空虚状态,消费者 C1 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C2 须挂起等待。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    仓库当前处于空虚状态,消费者 C3 须挂起等待。
    仓库当前处于空虚状态,消费者 C1 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C3 须挂起等待。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    仓库当前处于空虚状态,消费者 C2 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    仓库当前处于空虚状态,消费者 C3 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C3 须挂起等待。
    仓库当前处于空虚状态,消费者 C2 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C3 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    输入物品,仓库当前有 1 个物品。
    输入物品,仓库当前有 2 个物品。
    输出物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C2 须挂起等待。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C2 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    仓库当前处于空虚状态,消费者 C3 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    仓库当前处于空虚状态,消费者 C1 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C4 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C1 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
    仓库当前处于空虚状态,消费者 C3 须挂起等待。
    输入物品,仓库当前有 1 个物品。
    输出物品,仓库当前有 0 个物品。
  • 相关阅读:
    安卓机-华为安装charles证书
    sed替换文件内容
    升级php5.3.10到php5.6.30
    js 判断设备
    element-ui框架富文本编辑器
    git从主分支上拉取新分支以及提交代码、合并到主分支
    前端项目初始化
    vue路由点击第二次时报错
    js 数组sort方法根据数组中对象的某一个属性值进行排序
    去除一个数组中与另一个数组中的相同元素
  • 原文地址:https://www.cnblogs.com/huey/p/5974380.html
Copyright © 2020-2023  润新知