# wait()、notify()、notifyAll()的使用你也不能忘了阿

# 线程的状态

参考Java线程的生命周期

# Java提供的管程方法

Java提供的管程方法必须在synchronized里面使用.

# wait()

wait()会让调用线程释放持有的锁, 并进入阻塞状态, 等待被唤醒.

Java提供了几种wait的重载方法, 具体如下

method desc
void wait() Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
void wait(long timeout) Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
void wait(long timeout, int nanos) Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.

设置时间的wait方法, 区别在于如果没有被notify或notifyAll唤醒, 也会自动被唤醒.

注意: wait(0)与wait()等效.

# notify()和notifyAll()

两者的释义如下:

method desc
void notify() Wakes up a single thread that is waiting on this object's monitor.
void notifyAll() Wakes up all threads that are waiting on this object's monitor.

两者的区别在于: notifyAll()是唤醒所有

# sleep()

线程进入Blocked状态, 不释放持有的锁, 但会释放CPU资源, 等待设置的时间结束, 则会进入Runnable状态.

不需要在synchronized中调用, 不属于管程的方法

# 等待通知机制的应用场景

# 实现简单的等待通知机制

public class WaitTest {
	//实例方法加锁
    public synchronized void waiting(){
        try {
            this.wait(0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
	
    public synchronized void notifyIt(){
        this.notifyAll();
    }

    static class  task implements Runnable{
        private WaitTest test;
        private AtomicInteger count;

        public task(WaitTest test, AtomicInteger count){
            this.test = test;
            this.count = count;
        }


        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                //累加count, 并输出结果到控制台
                System.out.println("count curr is "+ count.incrementAndGet());
                //每次累加都调用notifyAll()通知所有对象锁等待队列中的线程
                //notifyAll()放的位置不一样,会造成不同的效果,放for外面则累加完才通知等待队列中的线程
                test.notifyIt();
            }
        }
    }

    public static void main(String[] args) {
        WaitTest test = new WaitTest();
        //计数器count
        AtomicInteger count = new AtomicInteger(0);
        //启动一个线程任务-累加count100次
        new Thread(new task(test, count)).start();
        //while判断count是否>=100, 否则就一直调用wait()
        while(count.get() < 100){
            System.out.println("main thread waiting start");
            test.waiting();
            System.out.println("main thread waiting end");
        }
        System.out.println("code executed, count = "+count.get());
    }

}

# 实现生产者消费者

较为常见的场景是生产者消费者, 常见的是场景, 实现方式却不常用, 由于众多中间件及其他即开即用方案的流行, 早已不再自己造轮子去实现此方案了. 虽然不常用, 但场景是基础且经典的, 我们依然必须了解和掌握.

经常有一类业务需求说要当某某情况时, 做某某操作, 不满足条件时就等条件满足再做, 接到这个需求时, 第一时间可能想, 那就每隔一段时间轮询是否满足条件即可, 但细想就会发现, 其实这样时效性不好保障, 也很浪费资源.

其实这种业务模型就是生产者&消费者, 当条件满足时就通知消费者进行消费, 当没有消费内容时, 就等待, 直到生产者将其唤醒. 接下来我们利用wait()和notify()实现一个生产者消费者模式

public class ProducerAndConsumer {
	//公用队列
    private final ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
    //实例对象锁-生产者和消费者共同持有一把锁
    private final Object lock = new Object();
    //业务生产者的实现-此处抽象为生产随机数
    private final static Random r = new Random();

    private Producer createProducer(){
        return new Producer();
    }

    private Consumer createConsumer(){
        return new Consumer();
    }

    class Producer{

        //生产内容-不断构造生产的内容并放入公用队列中
        public void producer() throws InterruptedException {
            int count = 0;
            while (true){
                queue.add(r.nextInt(100));
                count++;
                //当有放入内容时, 立即通知消费者进行消费
                synchronized (lock){
                    lock.notifyAll();
                }
                //无关内容
                if(count%10==0){
                    TimeUnit.MILLISECONDS.sleep(5000);
                }
                if(count > 1000) break;
            }
        }

    }

    class Consumer{
        public void consumer() throws InterruptedException {
            //消费者一直while(true)进行消费
            while(true){
                //如果queue中没有内容则将线程变为WAITING状态
                if(queue.isEmpty()){
                    synchronized (lock){
                        lock.wait();
                    }
                }else{
                    //消费queue中的内容
                    System.out.println("消费了"+queue.poll());
                    TimeUnit.MILLISECONDS.sleep(1000);
                }
            }
        }
    }

    public static void main(String[] args) {
        ProducerAndConsumer test = new ProducerAndConsumer();
        Producer producer = test.createProducer();
        Consumer consumer = test.createConsumer();
		
        //异步启动生产者
        new Thread(()->{
            try {
                producer.producer();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

		//异步启动消费者
        new Thread(()->{
            try {
                consumer.consumer();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        //检查queue中任务的数量
        new Thread(()->{
            try {
                while (true){
                    System.out.println("queue size = "+test.queue.size());
                    TimeUnit.SECONDS.sleep(2);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

}

# 简析wait()和notify()原理

# 参考

https://www.cnblogs.com/paddix/p/5381958.html

修改于: 8/11/2022, 3:17:56 PM