并发编程:等待与通知(wait,notify,notifyAll)


前言

一个线程A调用了对象O的wait()方法进入等待状态,另外一个线程B调用对象O的notify()或者notifyAll(),线程A收到通知后从对象的wait()返回,执行后续操作。线程之间通过对象O来完成交互,对象O的wait()、notify()、notifyAll()如同开关信号一样,用来完成等待方和通知方的交互工作


一、相关方法

notify():
通知一个在对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获取到了对象的锁,没有获得锁的线程重新进入WAITING状态。

notifyAll():
通知所有等待在该对象上的线程

wait()
调用该方法的线程进入 WAITING状态,只有等待另外线程的通知或被中断才会返回.需要注意,调用wait()方法后,会释放对象的锁

wait(long)
超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回

wait (long,int)
对于超时时间更细粒度的控制,可以达到纳秒

二、使用的标准范式

1.等待方

1.获取对象的锁
2.如果条件不满足,调用对象的wait()方法,被通知后仍要检查条件
3.条件满足执行对应的业务逻辑

synchronized(obj){
	while(条件不满足){
		obj.wait();
	}
	//TODO 实现业务逻辑
}

2.通知方

1.获取对象的锁
2.改变条件
3.通知所有等待在线程上的线程

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

三、实战

1.生产者/消费者模式

/**
 * 自定义实现一个有界阻塞队列
 * @param <T>
 */
public class CustomerBlockingQueue<T> {

    private int max;

    private int count;

    private final Object lock = new byte[1];

    private LinkedList<T> linkedList = new LinkedList<>();

    public CustomerBlockingQueue(int max) {
        this.max = max;
    }

    public void put(T t) throws InterruptedException {
        synchronized (lock) {
            while (count >= max) {
                lock.wait();
            }
            linkedList.add(t);
            System.out.println("new Obj add -> " + t);
            count++;
            lock.notifyAll();
        }
    }

    public T get() throws InterruptedException {
        T t;
        synchronized (lock) {
            while (count <= 0) {
                lock.wait();
            }
            t = linkedList.getFirst();
            linkedList.removeFirst();
            count--;
            lock.notifyAll();
        }
        return t;
    }
}

class Test {
    public static void main(String[] args) {
        final CustomerBlockingQueue<Integer> queue = new CustomerBlockingQueue<Integer>(6);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            executorService.submit(new Put(i, queue));
        }
        try {
            System.out.println("开始阻塞,已放部分数据");
            Thread.sleep(3000);
            System.out.println("结束阻塞,准备GET");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 20; i++) {
            executorService.submit(new Runnable() {
                @Override
                @SneakyThrows
                public void run() {
                    System.out.println("get obj -> " + queue.get());
                }
            });
        }
    }


}

class Put implements Runnable {

    int obj;

    CustomerBlockingQueue<Integer> queue;

    public Put(int obj, CustomerBlockingQueue<Integer> queue) {
        this.obj = obj;
        this.queue = queue;
    }

    @SneakyThrows
    @Override
    public void run() {
        queue.put(obj);
    }
}

2.交叉运行

/**
 * 启动两个线程, 一个输出 1,3,5,7…99, 另一个输出 2,4,6,8…100 最后 STDOUT 中按序输出 1,2,3,4,5…100
 */
public class Demo01 {

    private static Integer num = 1;

    private static class Print implements Runnable {
        @Override
        @SneakyThrows
        public void run() {
            while (num <=100) {
                synchronized (this) {
                    this.notifyAll();

                    String threadName = Thread.currentThread().getName();
                    System.out.println(this.getClass().getName()+ "<<<" + threadName + ":" + num);
                    num++;

                    this.wait();

                }
            }

        }
    }


    public static void main(String[] args) {
        Print print = new Print();
        new Thread(print).start();
        new Thread(print).start();
    }


}

四、注意点

1.notify和notifyAll应该用谁

尽可能使用notifyAll(),因为notify()随机唤醒一个等待在对象上的线程,无法保证唤醒的是我们需要的线程

2.为什么 wait() 方法需要写在 while 里,而不是 if?

以有界的阻塞队列来说:

synchronized (lock) {
	while (count >= max) {
	    lock.wait();
	}
	linkedList.add(t);
	System.out.println("new Obj add -> " + t);
	count++;
	lock.notifyAll();
}

当队列长度达到max时,那么两个生产线程a,b都进入等待,当被唤醒时,如果将while改为if,那么将会执行后续添加元素的业务逻辑,队列的元素会超过max值,将不再是有界队列

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值