• JAVA多线程之生产消费模型


    生产消费模型

    所谓生产消费模型,是通过一个容器来解决生产者和消费者的强耦合问题。通俗的讲,就是生产者不断的生产,
    消费之也在不断消费,消费者消费的产品是生产者生产的,这就必然存在一个中间的容器,我们可以把这个容器
    想象成一个仓库,当仓库没有满的时候,生产者生产产品,当仓库满了的时候,生产者不能继续生产产品,而需
    处于等待状态。当仓库不为空的时候,消费者可以消费产品,当仓库为空的时候,消费者不能再消费产品,而应
    处于等待状态。这样不断循环,在这个过程中,生产者和消费者是不直接杰出的,所谓的仓库其实就是一个阻塞
    队列,生产者生产的产品不直接提供给消费者,而是提供给阻塞队列,这个阻塞队列就是来解决生产者和消费者
    之间的强耦合,这就是生产者消费者模型。

    wait()方法

    1、注意wait()是Object里面的方法,而不是Thread里面的。它的作用是将当前线程置于预执行队列,并在wait()
    所在的代码处停止,等待唤醒通知。
    2、wait()只能在同步代码块或同步方法中执行,如果调用wait()方法,而没有持有适当的锁,就会抛出异常。
    wait()方法调用后会释放出锁,线程与其他线程竞争重新获取锁。
    3、线程调用了wait()方法后一直在等待,不会继续往下执行,wait()一旦执行,除非接受到唤醒操作或者异常
    中断,否则不会往下执行

    notify()方法

    1、notify()方法也是要在同步代码块或者同步方法中调用的,它的作用是使停止的线程继续执行,调用notify()
    方法后,会通知那些等待当前线程对象锁的线程,并使它们重新获取该线程的对象锁,如果等待线程比较多的时候,
    则由线程规划器随机挑选出一个呈现wait状态的线程。
    2、notify()调用之后不会立即释放锁,而是当执行notify()的线程执行完成,即退出同步代码块或同步方法时,
    才会释放对象锁。

    notifyAll()

    唤醒所有处于等待状态的线程,一般使用notifyAll()比较多,因为notify随机唤醒一个线程,可能不是我们想要的
    造成程序出现问题,notifyAll()唤醒所有等待线程则一定会得到我们想要的

    例程

    测试1,不加入同步和线程通信

    Test类

    package com.lding.test2;
    
    public class Test {
        public static void main(String[] args) {
            Queue queue=new Queue();
            new Thread(new Producer(queue)).start();
            new Thread(new Consumer(queue)).start();
        }
    }
    
    

    Queue类

    package com.lding.test2;
    
    public class Queue {
        private int n;
    
        public int getN() {
            System.out.println("消费:"+n);
            return n;
        }
    
        public void setN(int n) {
            System.out.println("生产:"+n);
            this.n = n;
        }
    }
    
    

    Producer类

    package com.lding.test2;
    
    public class Producer implements Runnable{
        Queue queue;
        Producer(Queue queue){
            this.queue=queue;
        }
        @Override
        public void run() {
            int i=0;
            while (true){
                queue.setN(i++);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    Consumer类

    package com.lding.test2;
    
    public class Consumer implements Runnable{
        Queue queue;
        Consumer(Queue queue){
            this.queue=queue;
        }
        @Override
        public void run() {
            while (true){
                queue.getN();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    运行结果

    可以看到,0生产了一次却消费了两次。

    测试2 加上同步

    Queue类

    package com.lding.test2;
    
    public class Queue {
        private int n;
    
        public synchronized int getN() {
            System.out.println("消费:"+n);
            return n;
        }
    
        public synchronized void setN(int n) {
            System.out.println("生产:"+n);
            this.n = n;
        }
    }
    
    


    生产了18并没有消费
    生产了22消费了两次22

    版本3 加上线程之间的通信

    Queue类

    package com.lding.test2;
    
    public class Queue {
        private int n;
        boolean flag=false;//flag=false 容器中无数据, flag=true 容器中有商品
        public synchronized int getN() {
            if(!flag){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("消费:"+n);
            flag=false;//消费完毕 容器没数据
            notifyAll();
            return n;
        }
    
        public synchronized void setN(int n) {
            if(flag){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("生产:"+n);
            this.n = n;
            flag=true;//生产完毕 容器中有数据
            notifyAll();
        }
    }
    
    

    运行结果完全是生产一个,消费一个,不会出现生产一次消费两次或者生产完不消费的情况。

    你以为的极限,也许只是别人的起点
  • 相关阅读:
    Oracle条件分支查询
    Oracle分组函数实例
    使用JDK合成照片
    Oracle存储过程记录异常日志
    Emmet Cheat Sheet(Sublime编辑)
    JS中级
    JS中级
    架构师必须知道的架构设计原则
    JS中级
    JS中级
  • 原文地址:https://www.cnblogs.com/LengDing/p/15118084.html
Copyright © 2020-2023  润新知