synchronized 是 Java 中罕用的锁机制,synchronized+Object.wait/notify
是罕用的期待唤醒机制,那它们的实现原理是什么呢?本文就 synchronized 与 Object.wait/notify 为例谈谈以下内容。
- synchronized 锁降级。
- synchronized 是如何工作的。
- synchronized 同步队列和期待队列是如何配合的。
- 为什么 Object.wait 必须在 synchronized 中。
synchronized 在 JVM 里的实现是基于进入和退出 Monitor 对象来实现办法同步和代码块同步的。monitor enter
指令是在编译后插入到同步代码块的开始地位,而 monitor exit
是插入到办法完结处和异样处,JVM 要保障每个 monitor enter
必须有对应的 monitor exit
与之配对。任何对象都有一个 monitor 与之关联,当且一个 monitor 被持有后,它将处于锁定状态。
Java 1.6 为了缩小取得锁和开释锁带来的性能耗费,引入了“偏差锁”和“轻量级锁”,在 Java SE 1.6 中,锁一共有 4 种状态,级别从低到高顺次是:无锁状态 、 偏差锁状态 、 轻量级锁状态 和重量级锁状态,这几个状态会随着竞争状况逐步降级。
留神:synchronized 能够润饰某个办法,也能够润饰某个对象。
执行 monitor enter 指令如果获取锁不胜利,会将该线程增加到该 monitor 对象对应的同步队列 SynchronizedQueue,当占用该 monitor 对象的线程执行 monitor out 时就会唤醒同步队列中的线程。
那么问题来了,唤醒线程时是抉择同步队列上哪一个线程呢?java8 默认抉择最近增加到同步队列中的那个线程。能够应用如下代码验证:
private static Object obj = new Object();
public static void main(String[] args) throws Exception {synchronized (obj) {newThread("t1");
newThread("t2");
newThread("t3");
newThread("t4");
newThread("t5");
Thread.sleep(1000);
System.out.println("main release");
}
System.out.println("main end");
Thread.sleep(2000);
}
private static void newThread(String name) throws Exception {new Thread(() -> {synchronized (obj) {System.out.println(Thread.currentThread().getName() + "get");
}
}, name).start();
Thread.sleep(100L);
}
复制代码
输入后果为:
同步队列和期待队列
晓得了 synchronized 基本概念之后,一起来看下Object.wait()/notify()
,Monitor 对象 中蕴含一个同步队列(由 _cxq
和 _EntryList
组成)和一个期待队列(_WaitSet
)。
- 被 notify 或 notifyAll 唤醒时依据 policy 策略抉择退出的队列(policy 默认为 0)
- 退出同步块时依据 QMode 策略来唤醒下一个线程(QMode 默认为 0)
留神:synchronized 的同步队列和期待队列 与 基于 AQS(lock/condition)
的同步队列和期待队列实现原理相似,只不过前者是一个同步队列对应一个期待队列,而后者是一个同步队列能够对应多个期待队列。
synchronized 的同步队列和期待队列流程图如下所示:
到这里为止,咱们大抵理解了 synchronized 是如何工作的,以及它的同步队列和期待队列是如何配合的。
最初咱们思考 2 个问题:
1、为什么要有 Object.wait/notify,只应用 synchronized 不能也能实现线程阻塞而后被其余线程唤醒(告诉)么?
2、执行 Object.wait/notify,为什么必须要在 synchronized 中呢?
这里小伙伴们略微劳动片刻,思考下上述 2 个问题的起因。
对于第一个问题,只应用 synchronized 是为了简略的加解锁,而应用 Object.wait/notify 是为了实现被告诉后,再执行下一步动作的逻辑。如果 Object.wait 后没有任何逻辑的话,这里的 Object.wait 是没有意义的,能够间接去掉。
对于第二个问题,为什么必须要在 synchronized 中呢,咱们都晓得 synchronized 锁机制大都是为了保障原子性,因而咱们有理由置信,这里的起因大概率就是原子性。那么是为了什么的原子性呢?是为了保障在 Object.wait 时,将以后线程增加到_WaitSet 期待队列和 monitor out 唤醒其余线程的原子性;为了保障在 Object.notify 时,在将以后线程从 WaitSet 移到同步队列时,锁相干的线程状态不会发生变化。
参考:《2020 最新 Java 根底精讲视频教程和学习路线!》
链接:https://juejin.cn/post/694228…