wait和notify

等待/通知的经典范式

等待方(消费者):

synchronized(对象) {
  while(条件不满足){
    对象.wait();	// wait线程被唤醒时,从这一行开始执行。如果前面用的是if判断,不会再次判断条件是否满足,而是直接执行后续逻辑,当多个线程都这样的话,可能导致资源不可用;用while判断,会再次判断条件是否满足。
  }
  条件满足时的处理逻辑;
}

通知方(生产者):

synchronized(对象) {
  改变条件;
  对象.notifyAll();
}

wait()为什么要处于while循环中:

当多个线程并发访问同一个资源的时候, 若消费者同时被唤醒,但是只有一个资源可用, 那么if会导致资源被用完后直接去获取资源(发生越界异常等),而while则会让每个消费者获取之前再去判断一下资源是否可用.可用则获取,不可用则继续wait。

等待超时模式

等待方(消费者):

long future = System.currentTimeMillis() + mills;
long remaining = mills;
synchronized (lock) {
	while (!condition && remaining > 0) {
		wait(remaining);
        remaining = future - System.currentTimeMillis();
    }
    //处理代码
}

为什么需要remaining?仅靠wait(long)控制不行吗?

  • wait()在什么时候返回?wait()在接收到其他线程发出的notify通知后,会被唤醒(注意,这里仅仅是唤醒,不是返回),由等待队列(WaitingQueue)转移到阻塞队列(BlockedQueue),和其他线程去竞争锁。如果获得了锁,那么返回;如果未获得锁,继续阻塞。

  • wait(long)在什么时候返回?wait(long)在接收到notify通知或者到了指定的超时时间后,会被唤醒,由超时等待队列(TimedWaitingQueue)转移到阻塞队列(BlockedQueue),和其他线程去竞争锁。如果获得了锁,那么返回;如果未获得锁,继续阻塞。

  • 所以wait(long)并不是在经过指定时间后就一定能够返回(经过指定时间后仅仅是被唤醒,还需要继续去竞争锁,获得锁后才能返回,没获得锁时继续阻塞),所以后面需要加一个针对超时时间的判断。但这样也不能保证在指定的超时时间内返回。

package wait;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author: lixingzhong
 * @date: 2021/9/29 2:31 下午
 */
public class Main {

    public static final Queue<Integer> queue = new LinkedBlockingQueue<Integer>();

    public static void main(String[] args) {

        Thread consumer = new Thread(new Runnable() {
            public void run() {
                synchronized (queue) {
                    long remaining = 3000;
                    long future = System.currentTimeMillis() + remaining;
                    while (queue.isEmpty() && remaining > 0) {
                        try {
                            System.out.println(System.currentTimeMillis()/1000);	// 调用wait的时间戳
                            queue.wait(remaining);
                            remaining = future - System.currentTimeMillis();
                            System.out.println(remaining);
                        } catch (InterruptedException e) {
                            System.out.println("interrupted");
                        }
                    }
                    System.out.println(System.currentTimeMillis()/1000);	// 从wait返回的时间戳(比调用时间戳大5s,并不是指定的3s)
                    queue.poll();
                }
            }
        });

        Thread producer = new Thread(new Runnable() {
            public void run() {
                synchronized (queue) {
                    try {
                        Thread.sleep(5000);	// 如果将sleep放到synchronized外面,wait能够获取到锁并在3s的时候返回
                    } catch (InterruptedException e) {
                        System.out.println("interrupted");
                    }
                    queue.add(1);
                    queue.notifyAll();
                }
            }
        });

        consumer.start();
        producer.start();
    }
}

参考资料

  1. 并发编程之 wait()为什么要处于while循环中?

  2. 等待超时模式

  3. wait和wait(long timeout)的区别

  4. 多线程初级(中) - 知乎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值