笔记:Java Language Specification - 章节17- 线程和锁
回答一个问题:多线程场景下,有时一个线程对shared variable的修改可能对另一个线程不可见。那么,何时一个线程对内存的修改才会对另一个线程可见呢?
基本的原则: 如果 读线程 和 写线程 不进行同步,就不能保证可见性
地址:Oracle -- Java Language Specification -- Chapter 17. Threads and Locks 阐述了Java 内存模型下多线程程序的 Semantics
已经有人翻译好了: 深入分析 java 8 编程语言规范:Threads and Locks
14.19. The synchronized
Statement
注意: 每个object都有一个monitor lock,这就是内置锁。 synchronized 和 Object.wait() / Object.notify() / Object.notifyAll() 都指的是这把锁。
再明确:每把Mutex 实际上就是 1个open/closed 状态位 + 1个wait-queue (维护着排队等待这把锁而被blocked的threads)
; A mutex may either be open or closed. ; It also contains a queue of threads that are waiting for the mutex to become open (define-datatype Mutex Mutex? ($a-mutex (ref@closed? reference?) ; ref to bool (ref@wait-queue reference?))) ; ref to (listof thread)
所以,obj 和 mutex 就是同义词。
一个object作为内置锁,通过 synchronized 来访问这把锁,而 执行 obj.wait() 会让本线程进入 obj的wait-queue中,obj.notify() 会“发送消息” 给该obj的 wait-queue上的所有threads,
synchronized (o) {...} === lock(o) ; {...} ; unlock(o)
o.wait() === wait(o) // 靠,面向对象的写法看起来有点怪怪的,明明语义是 让本线程去wait(o) ,写成 o.wait() 还容易让人以为是要对 o做点什么呢!
o.notify() === nofity(o)
也就是说 o 的 wait-queue 中存在3种情况的线程:
1. 因为执行 lock(o) 而被阻塞的线程。 这种线程需排队等到锁、才能进入Runnable状态被调度执行。
2. 因为 wait(o) 而加入wait-queue、等待其他线程 notify(o) 的线程,
3. 因为 wait(o, time) 而加入wait-queue、限时等待其他线程 notify(o) 的线程
wait、notify、notifyAll三者的异同: (假设有对象A,线程T)
1. 是Object类的实例方法,且是native方法
2. T调用A的这些方法时必须先拥有A的对象锁,即只能在同步方法或同步块中调用这些方法。否则会抛出IllegalMonitorStateException(是RuntimeException故无需try catch)
3. 调用wait方法并不是立马释放锁,要等同步方法或同步块执行完;
同理,调用notify、notifyAll并不会有线程立马获取到对象锁,需要等调用者所在同步方法或同步块执行完。
4. 可以理解为每个对象有个wait set(等待池)和monitor set(锁池)用来存放线程:
———— wait set中线程等待收到通知信号(notify、notifyAll)、不会竞争获取A的对象锁,
———— monitor set中的线程则竞争对象锁;
T调用A的wait方法则T进入wait set、
T调用notify或notifyAll则wait set中的一个线程(随机选)或所有线程进入monitor set竞争对象锁。
(从这可看出,第一个获得对象锁的wait线程执行完后,若没有继续调用该对象的notify或notifyAll,且monitor set中没有线程,则其他wait线程仍一直等待,即便对该对象锁已经空闲)