1. 线程的状态
Thread.State 枚举,将线程划分为六中状态。
new
线程刚被创建,但是还没有调用 start() 方法。
RUNNABLE
当调用了 start() 方法之后,注意,Java Api 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】、【阻塞状态】(由于 BIO导致的线程阻塞,在 java 里无法区分,依然认为是可运行状态)。
TERMINATEED
终止状态,线程执行完毕了,不会再转化为其他任何状态。
Java API 层面对【阻塞状态】又细分为 BLOCKED
、WAITING
、 TIMED_WAITING
。
2. Monitor
Monitor 是操作系统中的一个锁对象,synchronized(Obj){...}
给代码块加重量级锁就是将 obj 和 操作系统中的 monitor 对象关联起来。怎么关联呢? object 的 markword 字段中存储了monitor 对象的地址。
Monitor 主要有三个属性:
- Owner:存储获得锁的线程,而且只能存储一个线程。
- EntryList:竞争锁失败的线程都会进入这个等待队列,陷入
BLOCKED
状态。(等待正拿着锁的线程释放锁) - WaitSet:线程获得过锁,但不满足运行条件的线程,陷入
WAITING
状态。
场景:
- 刚开始 Monitor 中的 Owner 为 null,当前锁不属于任何线程。
- 当 Thread-2 执行 synchronize(obj) 就会将 Monitor 的 Owner 设置为 Thread-2。
- Thread-2 还未执行完毕,Thread-3、Thread-4、Thread-5 分别执行了 synchronize(obj) ,这三个线程都拿锁失败了,进入 EntryList 中陷入
BLOCKED
状态。 - Thread-2 执行完同步代码块中的内容,然后唤醒 EntryList 中的全部线程,这三个线程开竞争锁,谁抢到,谁执行,竞争失败的继续 BLOCKED。
- Thread-0,Thread-2 之前拿到过锁,但是因为执行条件不满足,进入了 WaitSet,陷入 WAITING 状态。后面要通过 wait-notify 机制唤醒。
- 进入 WaitSet 中的线程会在 Owner线程调用notify 或 notifyAll 时被唤醒。但唤醒后不会立刻获取到锁,而是进入EntryList 中,重新竞争。
PS:
- 只有对统一 obj 加锁的线程会被同一个 Monitor 对象管理 或者 监视。
- 获得锁的线程(Owner 线程)发现条件不满足,调用 wait 方法,线程可立即进入 WaitSet 变成 WAITING 状态。
- EntryList 和 WaitSet (BLOCK 状态 和 WAITING状态)的线程不占用 CPU 的时间片。
- TIMED_WAITING 意思是带有时间限制的“阻塞状态”。