wait和notify

Posted datartvinci

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了wait和notify相关的知识,希望对你有一定的参考价值。

  • 基本用法?

  • 说说wait?

  • 说说notify?

  • 为什么要synchronized?

1.简介

wait()和notify()是用于多线程之间协作的方法。如果一个线程调用了wait(),会阻塞直到其他线程调用了notify()。

技术图片

 

技术图片

https://www.baeldung.com/java-wait-notify

2.wait()

如果调用了某个对象的wait(),当前线程会阻塞,直到其他线程调用这个对象的notify()方法,当前线程才有机会继续运行。

为了通俗易懂,接下来通过synchronized获取monitor简称为获取锁。

当前线程必须获取锁才能调用wait(),如果是当前类的锁,则是wait(),如果是Object的锁,则是obj.wait()。调用wait()会立刻释放锁,然后阻塞。

一共有两种方法能让当前线程从阻塞状态恢复,然后继续运行:

  • 其他线程获取了这个对象的锁并调用了notify()或notifyAll(),且当前线程获取锁成功的前提下,继续执行下去。

  • 其他线程调用当前线程的"中断方法"interrupt(),且当前线程获取锁成功的前提下,当前线程则会捕获"中断异常"InterruptedException,然后继续执行下去。

3.notify()和notifyAll()

3.1.notify()

获取锁后调用notify(),则随机唤醒一个阻塞的线程。被唤醒的线程并不能立刻执行,处于Runnable状态,需要获取到锁成功,甚至与其他线程竞争同一个锁成功后,才能继续运行。

与wait()不同,调用notify()之后并不会立即释放锁。

3.2.notifyAll()

与notify()工作机制一样,但是唤醒所有线程,所有调用了wait()的线程都处于Runnable状态,谁先获取锁,谁就先执行。

4.为什么要synchronized

class BlockingQueue 
    Queue<String> buffer = new LinkedList<String>();
?
    public void give(String data) 
        buffer.add(data);
        notify();                   // Since someone may be waiting in take!
    
?
    public String take() throws InterruptedException 
        while (buffer.isEmpty())    // don‘t use "if" due to spurious wakeups.
            wait();
        return buffer.remove();
    

1.一个消费者线程调用take(),看到队列是空的

2.在消费者调用wait()之前,生产者线程过来调用了give()方法,此时队列非空

3.消费者线程调用wait(),进入阻塞状态

4.如果不再有生产者线程调用give(),那消费者线程就永远阻塞下去

显然,用synchronized来保证 条件判断(buffer.isEmpty())和wait()这两个操作之间不会有其他线程调用notify,就能解决这个问题。

https://stackoverflow.com/questions/2779484/why-must-wait-always-be-in-synchronized-block

能够使用synchronized解决这个问题的关键在于 wait()的调用会释放锁

  1. http://coding.derkeiler.com/Archive/Java/comp.lang.java.programmer/2006-01/msg01130.html

5.用法

编程范式

    
 synchronized (obj) 
         while (<condition does not hold>)
             obj.wait(timeout);
             
         ... // Perform action appropriate to condition
     
    synchronized (obj) 
        if(condition)
            obj.notify();
     

 

<https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html

wait和notify都需要在synchronized块中调用,否则抛出IllegalMonitorStateException。

条件判断为什么用while而不是if?因为从阻塞状态恢复之后,条件可能已经变化了,需要重新判断,代码较冗余:

synchronized (monitor) 
    //  判断条件谓词是否得到满足
    if(!locked) 
        //  等待唤醒
        monitor.wait();
        if(locked) 
            //  处理其他的业务逻辑
         else 
            //  跳转到monitor.wait(); 
        
    

生产者消费者模型

消费者:

private String message;
?
private boolean empty = true;    
public synchronized String take() 
        while (empty) 
            try 
                wait();
             catch (InterruptedException e) 
        
        empty = true;
        notifyAll();
        return message;
    

 

生产者:

   public synchronized void put(String message) 
        while (!empty) 
            try  
                wait();
             catch (InterruptedException e) 
        
        empty = false;
        this.message = message;
        notifyAll();
    

 

https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

使用wait和notify让两个线程交替打印数字

boolean isOdd = false;
    int i = 0;
?
    public synchronized void odd() 
        while (i <= 100) 
            while (isOdd) 
                try 
                    wait();
                 catch (InterruptedException e) 
                
            
            i++;
            isOdd = true;
            if(i>100)
                notify();
                break;
            
            System.out.println("odd" + i);
            notify();
        
    
?
    public synchronized void even() 
        while (i <= 100) 
            while (!isOdd) 
                try 
                    wait();
                 catch (InterruptedException e) 
                
            
            isOdd = false;
            i++;
            if(i>100)
                notify();
                break;
            
            System.out.println("even" + i);
            notify();
        
    

 

拓展:n个线程交替打印

    final Object obj = new Object();
    int num = 1;
    int lastThread = 0;
?
    class Task implements Runnable 
        final int index;
        final int n;
?
        Task(int i, int j) 
            index = i;
            n = j;
        
?
        @Override
        public void run() 
            synchronized (obj) 
                while (num <= 100) 
                    while (lastThread % n != index - 1) 
                        try 
                            obj.wait();
                         catch (InterruptedException e) 
                            e.printStackTrace();
                        
                    
                    if (num > 100) 
                        return;
                    
                    System.out.println("thread:" + index + ",print:" + num);
                    num++;
                    lastThread++;
                    obj.notifyAll();
                
            
        
    
?
    @Test
    public void testNThread() throws InterruptedException 
        int n = 10;
        for (int i = 1; i <= n; i++) 
            Thread thread = new Thread(new Task(i, n));
            thread.start();
            if (i == n) 
                thread.join();
            
        
    
 

 

以上是关于wait和notify的主要内容,如果未能解决你的问题,请参考以下文章

Java中 wait和await notify和signal的区别

关于wait,notify,notifyall,sleep方法的思考

Java线程的wait(), notify()和notifyAll()

多线程的wait/notify的使用

java的notify方法为啥也要同步

求求你,别再用wait和notify了!