对Java中monitor&wait notify&join原理的简单介绍

155 阅读1分钟

 ``` 本文已参与「新人创作礼」活动,一起开启掘金创作之路。


##  Monitor 原理

        Monitor 被翻译为监视器或管程。\
        每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针。\
        Monitor 结构如下。

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a13da8e536d44772870aa53817738285~tplv-k3u1fbpfcp-zoom-1.image)![](<> "点击并拖拽以移动")​编辑

         ● 刚开始 Monitor 中 Owner 为 null。\
         ● 当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner。\
         ● 在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入EntryList BLOCKED。\
         ● Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的。\
         ● 图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,后面讲wait-notify 时会分析 

> 注意:\
> synchronized 必须是进入同一个对象的 monitor 才有上述的效果\
> 不加 synchronized 的对象不会关联监视器,不遵从以上规则

## wait notify 原理

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/16d5f131435542eda643a7515b6578c5~tplv-k3u1fbpfcp-zoom-1.image)![](<> "点击并拖拽以移动")​编辑

         ● Owner 线程发现条件不满足,调用被上锁对象的wait 方法,即可进入 WaitSet 变为 WAITING 状态\
         ● BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片\
         ● BLOCKED 线程会在 Owner 线程释放锁时唤醒\
         ● WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入\
         ● EntryList 重新竞争

## join原理

### 作用:等待线程执行结束。是调用者轮询检查线程 alive 状态。

        以下例子中,t1睡10秒,执行到t1.wait(0),即使主线程已放弃t1对象的monitor,但是synchronized代码块还未执行完,主线程就不能执行下去。直到while循环结束,即t1线程已执行完毕,死亡。**synchronized代码块执行完毕,主线程继续向下执行。**

        t1线程执行完毕后好像还会唤醒主线程,这个说法有待商榷。一位博主的解释:[掘金](https://juejin.cn/post/6844903624842149895 "掘金")

**t1.join();**

等价于

> ```
> synchronized (t1) {
> // 调用者线程进入 t1 的 waitSet 等待, 直到 t1 运行结束
>             while (t1.isAlive()) {
>                 t1.wait(0);
>             }
>         }
> ```

举例

//  在主线程调用t1的join方法,等价于把主线程放入t1的waitSet进行等待

@Slf4j public class test {

static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
    test2();
}
private static void test2() throws InterruptedException {
    Thread t1 = new Thread(() -> {
        try {
            sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        r1 = 10;
    });
    Thread t2 = new Thread(() -> {
        try {
            sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        r2 = 20;
    });
    long start = System.currentTimeMillis();
    t1.start();
    t2.start();
    t1.join();    //在主线程调用t1的join方法,等价于把主线程放入t1的waitSet进行等待
    t2.join();
    long end = System.currentTimeMillis();
    log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}

}


![](<> "点击并拖拽以移动")

​