码迷,mamicode.com
首页 > 其他好文 > 详细

wait和notify

时间:2019-06-01 09:38:25      阅读:131      评论:0      收藏:0      [点我收藏+]

标签:index   两种方法   竞争   href   buffer   条件   oca   随机   不同   

  • 基本用法?

  • 说说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

标签:index   两种方法   竞争   href   buffer   条件   oca   随机   不同   

原文地址:https://www.cnblogs.com/datartvinci/p/10958270.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!