关于多线程:工作三年小胖连-waitnotifynotifyAll-都不会用真的菜

126次阅读

共计 3481 个字符,预计需要花费 9 分钟才能阅读完成。

前几篇温习了下线程的创立形式、线程的状态、Thread 的源码这几篇文章,这篇讲讲 Object 几个跟线程获取开释锁相干的办法:wait、notify、notifyAll。

wait 办法源码解析

因为 wait() 是 Object 类的 native 办法,在 idea 中,它长这样:

public final native void wait(long timeout) throws InterruptedException;

看不了源码,那只能看源码的正文,正文太长,我摘取一些要害的点进去:

1、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.

The current thread must own this object's monitor.

2、In other words,waits should always occur in loops.like this one:
synchronized(obj) {while (condition does not hold)
        obj.wait(timeout);
    // Perform action appropriate to condition
}

3、@throws IllegalArgumentException
if the value of timeout isnegative.

@throws IllegalMonitorStateException
if the current thread is notthe owner of the object 's monitor.

@throws InterruptedException
if any thread interrupted the current thread before or while the current thread was waiting
for a notification.
The interrupted status of the current thread is cleared when this exception is thrown.

正文中提到几点:

  • wait 会让以后线程进入期待状态,除非其余线程调用了 notify 或者 notifyAll 办法唤醒它,又或者等待时间到。另外,以后线程必须持有对象监控器(也就是应用 synchronized 加锁)
  • 必须把 wait 办法写在 synchronized 爱护的 while 代码块中,并始终判断执行条件是否满足,如果满足就往下继续执行,如果不满足就执行 wait 办法,而在执行 wait 办法之前,必须先持有对象的 monitor 锁,也就是通常所说的 synchronized 加锁。
  • 超时工夫非法,抛 IllegalArgumentException 异样;不持有对象的 monitor 锁,抛 IllegalMonitorStateException 异样;在期待期间被其余线程中断,抛出 InterruptedException 异样。

为什么 wait 必须在 synchronized 爱护的同步代码中应用?

逆向思考下,没有 synchronized 爱护的状况下,咱们应用会呈现啥问题?先试着来模仿一个简略的生产者消费者例子:

public class BlockingQueue {Queue<String> buffer = new LinkedList<String>();

    // 生产者,负责往队列放数据
    public void give(String data) {buffer.add(data);
        notify();}

    // 消费者,次要是取数据
    public String take() throws InterruptedException {while (buffer.isEmpty()) {wait();
        }
        return buffer.remove();}

}

首先 give () 往队列里放数据,放完当前执行 notify 办法来唤醒之前期待的线程;take () 查看整个 buffer 是否为空,如果为空就进入期待,如果不为空就取出一个数据 。但在这里咱们并没有用 synchronized 润饰。假如咱们当初 只有一个生产者和一个消费者,那就有可能呈现以下状况:

  • 此时,生产者无数据。消费者线程调用 take(),while 条件为 true。失常来说,这时应该去调用 wait() 期待,但此时消费者在调用 wait 之前,被被调度器暂停了,还没来得及调用 wait。
  • 到生产者调用 give 办法,放入数据并视图唤醒消费者线程。可这个时候唤醒不起作用呀。消费者并没有在期待。
  • 最初,消费者回去调用 wait 办法,就进入了有限期待中。

看明确了吗?第一步时,消费者判断了 while 条件,但真正执行 wait 办法时,生产者已放入数据,之前的 buffer.isEmpty 的后果曾经过期了,因为这里的“判断 – 执行”不是一个原子操作,它在两头被打断了,是线程不平安的

正确的写法应该是这样子的:以下写法就确保永远 notify 办法不会在 buffer.isEmpty 和 wait 办法之间被调用,也就不会有线程平安问题。

public class BlockingQueue {Queue<String> buffer = new LinkedList<String>();

    // 生产者,负责往队列放数据
    public void give(String data) {synchronized(this) {buffer.add(data);
            notify();}
    }

    // 消费者,次要是取数据
    public String take() throws InterruptedException {synchronized(this) {while (buffer.isEmpty()) {wait();
            }
            return buffer.remove();}
    }

}

notify & notifyAll

notify & notifyAll 都是 Object 的 native 办法,在 IDEA 中看不到它的源码,同样是只能看正文。

public final native void notify();

public final native void notifyAll();

正文中次要提到以下几点:

  • notify() 随机唤醒一个期待该对象锁的线程,即便是多个也随机唤醒其中一个(跟线程优先级无关)。
  • notifyAll() 告诉所有在期待该竞争资源的线程,谁抢到锁谁领有执行权(跟线程优先级无关)。
  • 以后线程不持有对象的 monitor 锁,抛 IllegalMonitorStateException 异样。

为啥 wait & notify & notifyAll 定义在 Object 中,而 sleep 定义在 Thread 中?

两点起因:

  • Java 的每个对象都有一把称之为 monitor 监视器的锁,每个对象都能够上锁,所以在对象头中有一个用来保留锁信息的地位。这个锁是对象级别的,而不是线程级别的,每个对象都有锁,通过线程取得。如果线程须要期待某些锁那么调用对象中的 wait 办法就有意义了,它期待的就是这个对象的锁。如果 wait 办法定义在 Thread 类中,线程正在期待的是哪个锁就不显著了。简略来说,因为 wait & notify & notifyAll 是锁级别的操作,所以把他们定义在 Object 类中因为锁属于对象。
  • 再者,如果把它们定义在 Thread 中,会带来很多问题。一个线程能够有多把锁,你调用 wait 或者 notify,我怎么晓得你要期待的是哪把锁?唤醒的哪个线程呢?

wait 和 sleep 的异同

上次的文章咱们曾经看过了 sleep 的源码了,它们的相同点次要有:

  • 它们都能够扭转线程状态,让其进入计时期待。
  • 它们都能够响应 interrupt 中断,并抛出 InterruptedException 异样。

不同点:

  • wait 是 Object 类的办法,而 sleep 是 Thread 类的办法。
  • wait 办法必须在 synchronized 爱护的代码中应用,而 sleep 办法可在任意中央。
  • 调用 sleep 办法不开释 monitor 锁,调用 wait 办法,会开释 monitor 锁。
  • sleep 工夫一到马上复原执行(因为没有开释锁);wait 须要等中断,或者对应对象的 notify 或 notifyAll 才会复原,抢到锁才会执行(唤醒多个的状况)。

福利

如果看到这里,喜爱这篇文章的话,请帮点个难看。微信搜寻 一个优良的废人 ,关注后回复 电子书 送你 100+ 本编程电子书,不只 Java 哦,详情看下图。回复 1024送你一套残缺的 java 视频教程。

正文完
 0