任意线程对 Object(Object 由 synchronized 保护)的访问,首先要得到 Object 的监视器。若是获取失败,线程进入同步队列,线程状态变为 BLOCKED。当访问 Object 的
前驱(得到了锁的线程)释放了锁,则该释放操做唤醒阻塞在同步队列中的线程,使其从新尝试对监视器的获取。
回顾线程的竞争机制
再来回顾一下线程的竞争机制对于锁升级这块的一些基本流程。方便你们更好的理解加入有这样一个同步代码块,存在 Thread#一、Thread#2 等多个线程
synchronized (lock) {
// do something
}
状况一:只有 Thread#1 会进入临界区;
状况二:Thread#1 和 Thread#2 交替进入临界区,竞争不激烈;
状况三:Thread#1/Thread#2/Thread3… 同时进入临界区,竞争激烈
偏向锁
此时当 Thread#1 进入临界区时,JVM 会将 lockObject 的对象头 Mark Word 的锁标志位设为“01”,同时会用 CAS 操做把 Thread#1 的线程 ID 记录到 Mark Word 中,此时进
入偏向模式。所谓“偏向”,指的是这个锁会偏向于 Thread#1,若接下来没有其余线程进入临界区,则 Thread#1 再出入临界区无需再执行任何同步操做。也就是说,若只有
Thread#1 会进入临界区,实际上只有 Thread#1 初次进入临界区时须要执行 CAS 操做,之后再出入临界区都不会有同步操做带来的开销。
轻量级锁
偏向锁的场景太过于理想化,更多的时候是 Thread#2 也会尝试进入临界区, 若是 Thread#2 也进入临界区可是Thread#1 尚未执行完同步代码块时,会暂停 Thread#1而且升
级到轻量级锁。Thread#2 经过自旋再次尝试以轻量级锁的方式来获取锁
重量级锁
若是 Thread#1 和 Thread#2 正常交替执行,那么轻量级锁基本可以知足锁的需求。可是若是 Thread#1 和 Thread#2同时进入临界区,那么轻量级锁就会膨胀为重量级锁,意
味着 Thread#1 线程得到了重量级锁的状况下,Thread#2就会被阻塞
Synchronized 结合 Java Object 对象中的wait,notify,notifyAll
前面咱们在讲 synchronized 的时候,发现被阻塞的线程何时被唤醒,取决于得到锁的线程何时执行完同步代码块而且释放锁。那怎么作到显示控制呢?咱们就须要
借 助 一 个 信 号 机 制 : 在 Object 对 象 中 , 提 供 了wait/notify/notifyall,能够用于控制线程的状态
wait/notify/notifyall 基本概念
wait:表示持有对象锁的线程 A 准备释放对象锁权限,释放 cpu 资源并进入等待状态。
notify:表示持有对象锁的线程 A 准备释放对象锁权限,通知 jvm 唤 醒 某 个 竞 争 该 对 象 锁 的 线 程 X 。 线 程 Asynchronized 代码执行结束而且释放了锁以后,线程 X 直
接得到对象锁权限,其余竞争线程继续等待(即便线程 X 同步完毕,释放对象锁,其余竞争线程仍然等待,直至有新的 notify ,notifyAll 被调用)。
notifyAll:notifyall 和 notify 的区别在于,notifyAll 会唤醒全部竞争同一个对象锁的全部线程,当已经得到锁的线程A 释放锁以后,全部被唤醒的线程都有可能得到对象锁权限
须要注意的是:三个方法都必须在 synchronized 同步关键字 所 限 定 的 做 用 域 中 调 用 , 否 则 会 报 错java.lang.IllegalMonitorStateException ,意思是由于没有同步,因此
线程对对象锁的状态是不肯定的,不能调用这些方法。另外,经过同步机制来确保线程从 wait 方法返回时可以感知到感知到 notify 线程对变量作出的修改