终于明白阿里百度为什么拿WaitNotify通知机制考察求职者了

作为一名java程序员,相信都知道wait/notify的通知机制可以用来实现线程间通信。

求职面试时,不少公司都会出类似这样的题目。笔者有次面试时,面试官就问了这样一个问题:

wait/notify/notifyAll一般适用于什么场景?

由于笔者之前对这个知识点没有重视,面试官调侃说,你对WaitNotify通知机制都没有掌握,难怪你期望薪资那么低。

我们知道,wait表示线程的等待,调用该方法会导致线程阻塞,直至另一线程调用notify或notifyAll方法才可另其继续执行。经典的生产者、消费者模式即是使用wait/notify机制得以完成。

在了解wait/notify通知机制前,我们先回顾下java线程的生命周期。

今天我们针对此知识点,来编写个例子加深理解。

假设我开了一家水果实体店,日常经营主要有两项,一项是进货,一项是卖货。需要满足如下条件:

  1. 往店里进货,前提是店里库存不足,如果库存充足,则无需进货,直到把货卖出去了。

  2. 卖货,前提店里库存充足,库存不足时就要进货。

新建个水果店抽象类AbstractFruitStore

1public interface AbstractFruitStore {
2  void consumer(int num);
3
4  void producer(int num);
5}

水果实体店类实现AbstractFruitStore

 1/**
 2 * 水果店
 3 */
 4public class FruitStore implements AbstractFruitStore {
 5 private final int MAX_SIZE = 100;
 6 private LinkedList list = new LinkedList();
 7
 8 @Override
 9 public void consumer(int num) {
10    synchronized (list) {
11        //条件不满足,进入等待状态
12        while (num > list.size()) {
13            System.out.println("【要消费】:" + num + "\t【现库存量】:" + list.size() + "\t库存不足,暂不支持消费");
14            try {
15                //条件不满足,生产阻塞
16                list.wait();
17            } catch (InterruptedException e) {
18                e.printStackTrace();
19            }
20        }
21        //条件满足,开始消费
22        for (int i = 0; i < num; i++) {
23            list.remove();
24        }
25        System.out.println("【已消费】:" + num + "\t【现库存量】:" + list.size());
26        list.notifyAll();
27    }
28}
29
30@Override
31public void producer(int num) {
32    synchronized (list) {
33        while (list.size() + num > MAX_SIZE) {
34            System.out.println("【要生产】:" + num + "\t【库存已有】:" + list.size() + "\t库存充足,暂不支持生产");
35            try {
36                //条件不满足,生产阻塞
37                list.wait();
38            } catch (InterruptedException e) {
39                e.printStackTrace();
40            }
41        }
42        for (int i = 0; i < num; i++) {
43            list.add(new Object());
44        }
45        System.out.println("【已生产】:" + num + "\t【现库存量】:" + list.size());
46        list.notifyAll();
47    }
48  }
49}

生产者Producer

 1 /**
 2  * 生产者
 3  */
 4public class Producer extends Thread {
 5   private int num;
 6   public AbstractFruitStore abstractFruitStore;
 7
 8   public Producer(AbstractFruitStore abstractFruitStore) {
 9    this.abstractFruitStore = abstractFruitStore;
10   }
11
12   public void setNum(int num) {
13    this.num = num;
14   }
15
16   @Override
17   public void run() {
18    producer(num);
19   }
20
21   public void producer(int num) {
22    abstractFruitStore.producer(num);
23  }
24}

消费者Consumer

 1/**
 2 * 消费者
 3 */
 4public class Consumer extends Thread {
 5
 6    private int num;
 7
 8    private AbstractFruitStore abstractFruitStore;
 9
10    public Consumer(AbstractFruitStore abstractFruitStore) {
11    this.abstractFruitStore = abstractFruitStore;
12    }
13
14    public void run() {
15     consumer(num);
16    }
17
18    public void consumer(int num) {
19     abstractFruitStore.consumer(num);
20    }
21
22    public void setNum(int num) {
23     this.num = num;
24   }
25}

 1public class WaitNotifyTest {
 2 public static void main(String[] args) {
 3    AbstractFruitStore abstractFruitStore = new FruitStore();
 4    //消费者对象
 5    Consumer c1 = new Consumer(abstractFruitStore);
 6    Consumer c2 = new Consumer(abstractFruitStore);
 7    Consumer c3 = new Consumer(abstractFruitStore);
 8
 9    c1.setNum(10);
10    c2.setNum(20);
11    c3.setNum(30);
12
13    // 生产者对象
14    Producer p1 = new Producer(abstractFruitStore);
15    Producer p2 = new Producer(abstractFruitStore);
16    Producer p3 = new Producer(abstractFruitStore);
17    Producer p4 = new Producer(abstractFruitStore);
18
19    p1.setNum(20);
20    p2.setNum(30);
21    p3.setNum(50);
22    p4.setNum(80);
23
24    //启动消费者线程
25    c1.start();
26    c2.start();
27    c3.start();
28    //启动生产者线程
29    p1.start();
30    p2.start();
31    p3.start();
32    p4.start();
33  }
34}

运行结果:

从中我们可以得出,Wait-Notify场景一般和下面3个因素相关:

  1. 状态变量(State Variable)
    当线程需要wait时,是因为一些条件得不到满足导致的。例如往队列里填充数据,当队列元素已经满时,线程就需要wait停止运行。当队列元素有空缺时,再继续自己的执行。

  2. 条件断言(Condition Predicate)


    当线程确定是否进入wait或者是从notify醒来的时候是否继续往下执行,大部分都要测试状态条件是否满足。

  3. 条件队列(Condition Queue)
    每个对象都有一个内置的条件队列,当一个线程在该对象锁上调用wait函数的时候,就会将该线程加入到该对象的条件队列中。

总结

如果线程调用了Object对象的wait()方法,那么线程会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

当有线程调用了Object对象的notify()方法(只随机唤醒一个wait线程)或是notifyAll()方法(唤醒所有wait线程)被唤醒的的线程会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。

优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,只有线程再次调用wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了synchronized代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

一个知识点不仅能考验求职者的对该知识点的掌握程度,还延伸出其他知识点,考验求职者的知识面,难怪阿里百度这样的大公司喜欢在面试时拿它来考验求职者。

由于笔者水平有限,文中纰漏之处在所难免,权当抛砖引玉,不妥之处,请大家批评指正。

 

-END-

作者:洪生鹏  擅长java、Android、qt、小程序平台开发。技术交流、媒体合作、品牌宣传请添加微信: hsp-88ios

猜你喜欢

养成了定期更新简历的习惯,我不再像以前那么焦虑了

更多惊喜,请长按二维码识别关注

你若喜欢,别忘了帮忙点【在看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值