并发系列之「通过生产者消费者场景理解wait()/ notify() / notifyAll()」

wait(long timeout)/notify()/notifyAll()`

网上好多解释不是不全面就是有偏差,还是直接看官方解释

先来看一下源码:

public class Object {
  	public final native void notify();
  	public final native void notifyAll();
  	public final native void wait(long timeout) throws InterruptedException;
}

可见,三个方法皆是属于Object类的被final修饰的本地方法。

API中对wait(long timeout)的解释:

This method causes the current thread (call it <var>T</var>) to place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object. Thread <var>T</var> becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:
Some other thread invokes the {@code notify} method for this object and thread <var>T</var> happens to be arbitrarily chosen as the thread to be awakened.
Some other thread invokes the {@code notifyAll} method for this object.
Some other thread {@linkplain Thread#interrupt() interrupts} thread <var>T</var>.
The specified amount of real time has elapsed, more or less.  If {@code timeout} is zero, however, then real time is not taken into consideration and the thread simply waits until notified.

该方法导致当前线程(调用它T)将自己置于该对象的等待集中,然后放弃该对象上的任何和所有同步声明。为了线程调度的目的,线程T被禁用,处于休眠状态,直到发生以下四种情况之一:

  • 其他一些线程为该对象调用{@code notify}方法,而线程T碰巧被任意选择为要被唤醒的线程
  • 其他一些线程调用该对象的{@code notifyAll}方法
  • 其他线程{@linkplain thread #interrupt()中断}线程T
  • 指定的实时时间已经流逝,或多或少。但是,如果{@code timeout}为0,则不考虑实时,线程只是等待,直到收到通知。

API中对notify()的解释:

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the {@code wait} methods.
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.

唤醒在该对象监视器上等待的单个线程。如果有任何线程在等待该对象,则选择其中一个被唤醒。选择是任意的,由实现自行决定。线程通过调用一个{@code wait}方法来等待对象的监视器。

被唤醒的线程将无法继续执行,直到当前线程放弃该对象上的锁。被唤醒的线程将以通常的方式与其他可能在此对象上积极竞争同步的线程竞争;例如,被唤醒的线程在成为下一个锁定该对象的线程方面没有可靠的特权或缺点。

API中对notify()的解释:

Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the {@code wait} methods.
The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.

唤醒在该对象监视器上等待的所有线程。线程通过调用一个{@code wait}方法来等待对象的监视器。

被唤醒的线程将无法继续操作,直到当前线程放弃该对象上的锁。被唤醒的线程将以通常的方式与其他可能在此对象上积极竞争同步的线程竞争;例如,被唤醒的线程在成为下一个锁定该对象的线程时没有可靠的特权或缺点。

wait(long timeout) / notify() / notifyAll() 的使用

wait()使当前线程阻塞,前提是必须先获得锁,一般配合 synchronized 关键字使用,即,一般在synchronized 同步代码块里使用 wait()、notify/notifyAll() 方法。

由于 wait()、notify/notifyAll() 在 synchronized 代码块执行,说明当前线程一定是获取了锁的

  • 当线程执行wait()方法时,会释放当前的锁,然后让出CPU,进入等待状态;
  • 只有当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。

下面举个在生产者和消费者场景中的例子:

import java.util.LinkedList;
import java.util.Queue;

/**
 * @Author Hory
 * @Date 2020/10/19
 */
public class WaitTest {

    private static int maxsize = 2;
    private static Queue<Integer> queue = new LinkedList<>();

    public static void main(String[] args) {
        Thread producer = new Thread(new Runnable(){
            public void run(){
                while(true){
                    synchronized (queue) {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        System.out.println("producer: get queue lock");

                        while(queue.size() == maxsize){
                            System.out.println("queue is full, producer wait");
                            try {
                                queue.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        int num = (int) (Math.random()*100);
                        queue.offer(num);
                        System.out.println("producer: produce a element:" + num);
                        queue.notifyAll();
                        System.out.println("producer: exit a production process");
                    }
                }
            }
        });

        Thread consumer1 = new Thread(new Runnable(){
            public void run(){
                while(true){
                    synchronized (queue) {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("consumer1: get queue lock");

                        while(queue.isEmpty()){
                            System.out.println("queue is empty, consumer1 wait");
                            try {
                                queue.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        int num = queue.poll();
                        System.out.println("consumer1: consumer a element:" + num);
                        queue.notifyAll();
                        System.out.println("consumer1: exit a consumption process");
                    }
                }
            }
        });

        Thread consumer2 = new Thread(new Runnable(){
            public void run(){
                while(true){
                    synchronized (queue) {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("consumer2: get queue lock");

                        while(queue.isEmpty()){
                            System.out.println("queue is empty, consumer2 wait");
                            try {
                                queue.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        int num = queue.poll();
                        System.out.println("consumer2: consumer a element:" + num);
                        queue.notifyAll();
                        System.out.println("consumer2: exit a consumption process");
                    }
                }
            }
        });

        producer.start();
        consumer1.start();
        consumer2.start();
    }
}

运行如下:

producer: get queue lock
producer: produce a element:40
producer: exit a production process

producer: get queue lock
producer: produce a element:84
producer: exit a production process

producer: get queue lock  
queue is full, producer wait  

consumer2: get queue lock
consumer2: consumer a element:40
consumer2: exit a consumption process

consumer2: get queue lock
consumer2: consumer a element:84
consumer2: exit a consumption process

consumer2: get queue lock
queue is empty, consumer2 wait

consumer1: get queue lock
queue is empty, consumer1 wait

producer: produce a element:66
producer: exit a production process

如上,创建了一个生产者线程producer和两个消费者线程consumer1、consumer2,生产者负责向队列中offer元素,消费者负责poll元素,当生产者获得锁后,会先进行判断,如果queue满了,则执行wait()后进入阻塞队列并让出锁,如果queue没满,则不会进入while循环,也就不会执行while中的wait()(没有执行wait()就不会让出锁),则会一直向queue中offer元素,且每次添加之后都会调用queue.notifyAll();来唤醒所有等待的线程,重新参与锁的竞争。

消费者的运行过程同上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值