Monitor 原理

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

monitor&wait notify&join原理_intellij-idea

         ● 刚开始 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 原理

monitor&wait notify&join原理_java_02

调用被上锁对象的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线程执行完毕后好像还会唤醒主线程,这个说法有待商榷。一位博主的解释

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);
}


}