java并发编程Object.wait()、Object.notify()方法实现消费者(Consumer)- 生成者(Producer)模型

1.使用Object.wait(),Object.notify(),Object.notifyAll()

  • wait() 方法会使当前线程进入阻塞/睡眠状态,等待其他线程唤醒。并释放对这个对象的锁。,使用该方法需要同synchronized关键字配合使用,否则会抛出一个IllegalMonitorStateException异常信息。意思是当前线程必须要锁定这个对象后,才能使用这个对象的wait()方法。必须先获得对这个对象的访问权限也就是锁。
  • notify() 唤醒等待对这个对象的访问的线程。如果有多个的话,则唤醒最先等待的线程。同样需要和synchronized关键字配合使用。
  • notifyAll()唤醒所有等待对这个对象的访问的线程,但该方法并不能明确唤醒的顺序。
public class Test9 {

    public static Object object = new Object();

    public static void main(String[] args) {
        Test9 test9 = new Test9();
        MyThread1 myThread1_1 = test9.new MyThread1("线程1-1");
        myThread1_1.start();
        MyThread1 myThread1_2 = test9.new MyThread1("线程1-2");
        myThread1_2.start();
        MyThread1 myThread1_3 = test9.new MyThread1("线程1-3");
        myThread1_3.start();

        try {
            Thread.sleep(8000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        MyThread2 myThread2 = test9.new MyThread2();
        myThread2.start();
    }
    class MyThread1 extends Thread {
        public MyThread1(String name){
            super(name);
        }
        @Override
        public void run() {
            synchronized (object) {
                try {
                    System.out.println(Thread.currentThread().getName()+"阻塞等待,时间:"+System.currentTimeMillis());
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    object.notify();
                }
                System.out.println(Thread.currentThread().getName()+"该线程被唤醒了,并拥有对object的锁.时间:"+System.currentTimeMillis());
            }
        }
    }
    class MyThread2 extends Thread {
        @Override
        public void run() {
            synchronized(object){
                object.notify();
            }
        }
    }
}

---sout--- 可以看出在8秒后线程1_1被线程二唤醒了。
线程1-1阻塞等待,时间:1607563740121
线程1-3阻塞等待,时间:1607563740121
线程1-2阻塞等待,时间:1607563740121
线程1-1该线程被唤醒了,并拥有对object的锁.时间:1607563748123
		//当把线程二改为唤醒全部
        class MyThread2 extends Thread {
        @Override
        public void run() {
            synchronized(object){
                object.notifyAll();
            	}
       	 	}
    	}
---sout--- 可以看出在8秒后线程1的所有被线程二唤醒了,但并没有按先入先出。
线程1-1阻塞等待,时间:1607563934233
线程1-3阻塞等待,时间:1607563934234
线程1-2阻塞等待,时间:1607563934234
线程1-2该线程被唤醒了,并拥有对object的锁.时间:1607563942236
线程1-3该线程被唤醒了,并拥有对object的锁.时间:1607563942236
线程1-1该线程被唤醒了,并拥有对object的锁.时间:1607563942236

2.模拟生产-消费模型

当生产者在生产过程中消费者一直处于等待中,而当消费者在消费中生产者处于等待状态。也就是两者交换对临界资源的占有权。

public class Test2 {

    private static int queueSize = 5;
    //定义一个存放产品的容器队列
    private static PriorityQueue<String> producerQueue = new PriorityQueue(queueSize);

    public static void main(String[] args) {
        Test2 test = new Test2();
        Producer producer = test.new Producer("-生成者-");
        Consumer consumer = test.new Consumer("-消费者-");
        consumer.start();
        producer.start();
    }

    class Consumer extends Thread {

        public Consumer(String name) {
            super(name);
        }
        @Override
        public void run() {
            while (true) {
                synchronized (producerQueue) {
                    //判断是否还有产品
                    if (producerQueue.size() == 0) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "仓库已空,等待生产者生产产品");
                            //通知生产者
                            producerQueue.notify();
                            //使当前线程阻塞 释放对queue对象的锁,等待被唤醒
                            producerQueue.wait();

                        } catch (InterruptedException e) {
                            producerQueue.notify();
                        }
                    }
                    producerQueue.poll();
                    System.out.println(Thread.currentThread().getName() + "取走一个产品,仓库还剩" + producerQueue.size() + "个产品");
                }
            }
        }
    }

    class Producer extends Thread {

        public Producer(String name) {
            super(name);
        }
        @Override
        public void run() {
            while (true) {
                synchronized (producerQueue) {
                    //判断仓库是否已满
                    if (producerQueue.size() == queueSize) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "仓库已满,等待消费者消费完毕!");
                            //通知消费者
                            producerQueue.notify();
                            //使当前线程阻塞 释放对queue对象的锁,等待被唤醒
                            producerQueue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            producerQueue.notify();
                        }
                    }
                    producerQueue.offer("包子");
                    System.out.println(Thread.currentThread().getName() + "生产一个产品,放进仓库。产品个数:" + producerQueue.size());
                }
            }
        }
    }
}
---sout--- 
-消费者-仓库已空,等待生产者生产产品
-生成者-生产一个产品,放进仓库。产品个数:1
-生成者-生产一个产品,放进仓库。产品个数:2
-生成者-生产一个产品,放进仓库。产品个数:3
-生成者-生产一个产品,放进仓库。产品个数:4
-生成者-生产一个产品,放进仓库。产品个数:5
-生成者-仓库已满,等待消费者消费完毕!
-消费者-取走一个产品,仓库还剩4个产品
-消费者-取走一个产品,仓库还剩3个产品
-消费者-取走一个产品,仓库还剩2个产品
-消费者-取走一个产品,仓库还剩1个产品
-消费者-取走一个产品,仓库还剩0个产品
-消费者-仓库已空,等待生产者生产产品
-生成者-生产一个产品,放进仓库。产品个数:1
-生成者-生产一个产品,放进仓库。产品个数:2
-生成者-生产一个产品,放进仓库。产品个数:3
-生成者-生产一个产品,放进仓库。产品个数:4
-生成者-生产一个产品,放进仓库。产品个数:5
-生成者-仓库已满,等待消费者消费完毕!

3. jdk1.5版本Lock中引入了Condition

  • Condition是一个顶层接口,其中定义了如await()、await(long time, TimeUnit unit)signal()、signalAll()等方法,其作用和Object中的方法类似。

  • 官方在描述Condition引入了一个BoundedBuffer栗子模拟了生产者消费模式,通过lock中提供的方法newCondition()创建Condition,在栗子中可以看出put()、take()在对临界资源items操作时同样需要先获取到该对象锁。

    class BoundedBuffer {
        final Lock lock = new ReentrantLock();
        final Condition notFull  = lock.newCondition(); 
        final Condition notEmpty = lock.newCondition(); 
    
        final Object[] items = new Object[100];
        int putptr, takeptr, count;
     
        public void put(Object x) throws InterruptedException {
          lock.lock();
          try {
            while (count == items.length)
             notFull.await();
             items[putptr] = x;
             if (++putptr == items.length) 
                 putptr = 0;
             ++count;
             notEmpty.signal();
           } finally {
            //必须在此手动释放锁,否则异常后会造成死锁
            lock.unlock();
          }
        }
     
        public Object take() throws InterruptedException {
          lock.lock();
          try {
            while (count == 0)
             notEmpty.await();
             Object x = items[takeptr];
             if (++takeptr == items.length) 
                 takeptr = 0;
             --count;
             notFull.signal();
             return x;
           } finally {
             lock.unlock();
           }
        }
      }
    

4.阻塞队列BlockingQueue

*take(),put()方法就是通过Condition 来进行相互通知。

/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();

/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition(); 

public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

 public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }

因此本身阻塞队列就实现了生成消费模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

54hake

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值