Java-多线程

工作三年,小胖连 wait/notify/notifyAll 都

2021-03-01  本文已影响0人  JavaFish

前几篇复习了下线程的创建方式、线程的状态、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 必须在 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 修饰。假设我们现在只有一个生产者和一个消费者,那就有可能出现以下情况:

看明白了吗?第一步时,消费者判断了 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();

注释中主要提到以下几点:

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

两点原因:

wait 和 sleep 的异同

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

不同点:

小福利

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

资源 C语言 C++ Java Git Python GO Linux 经典必读 面试相关 前端 人工智能 设计模式 数据库 数据结构与算法 计算机基础
上一篇 下一篇

猜你喜欢

热点阅读