文章放置于:https://github.com/zgkaii/CS-Notes-Kz,欢迎批评指正!
1 线程状态简述
Java线程在运行的生命周期中可能处于如下6种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。
线程状态 | 说明 |
---|---|
NEW | 初始状态,线程刚被创建,但是并未启动(还未调用start方法)。 |
RUNNABLE | 运行状态,JAVA线程将操作系统中的就绪(READY)和运行(RUNNING)两种状态笼统地称为“运行中”。 |
BLOCKED | 阻塞状态,表示线程阻塞于锁。 |
WAITING | 等待状态,表示该线程无限期等待另一个线程执行一个特别的动作。 |
TIMED_WAITING | 超时等待状态,不同于WAITING的是,它可以在指定时间自动返回。 |
TERMINATED | 终止状态,表示当前状态已经执行完毕。 |
线程在自身的生命周期中,并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换。
![](https://img-blog.csdnimg.cn/20200929220833538.png)
在【并发编程基础】线程基础(常用方法、状态)一文中,主要学习了wait()、join()和sleep()等方法,在【并发编程】深入理解synchronized原理一文中,主要探讨了synchronized原理。下面就进行park()/unpark()、wait()/notify()/notifyAll()的学习。
2 wait和notify/notifyAll
2.1 源码简析
wait( ),notify( ),notifyAll( )都是Object基础类中的方法,所以在任何 Java 对象上都可以使用。
public class Object {
// 导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法。
public final void wait() throws InterruptedException {
wait(0);
}
// 导致当前线程等待,直到另一个线程调用此对象的notify()方法或notifyAll()方法,或者已经过了指定的时间。
public final native void wait(long timeout) throws InterruptedException;
// 唤醒正在此对象监视器上等待的单个线程。
public final native void notify();
// 唤醒等待此对象监视器的所有线程。
public final native void notifyAll();
}
打开objectMonitor.cpp,查看wait方法:
...
// create a node to be put into the queue
// Critically, after we reset() the event but prior to park(), we must check
// for a pending interrupt.
ObjectWaiter node(Self); // 将当前线程封装成ObjectWatier
node.TState = ObjectWaiter::TS_WAIT ; // 状态改为等待状态
Self->_ParkEvent->reset() ;
OrderAccess::fence(); // ST into Event; membar ; LD interrupted-flag
// Enter the waiting queue, which is a circular doubly linked list in this case
// but it could be a priority queue or any data structure.
// _WaitSetLock protects the wait queue. Normally the wait queue is accessed only
// by the the owner of the monitor *except* in the case where park()
// returns because of a timeout of interrupt. Contention is exceptionally rare
// so we use a simple spin-lock instead of a heavier-weight blocking lock.
Thread::SpinAcquire (&_WaitSetLock, "WaitSet - add") ;// 自旋操作
AddWaiter (&node) ;
Thread::SpinRelease (&_WaitSetLock) ; // 添加到_WaitSet节点中
...
查看AddWaiter()方法:
inline void ObjectMonitor::AddWaiter(ObjectWaiter* node) {
assert(node != NULL, "should not dequeue NULL node");
assert(node->_prev == NULL, "node already in list");
assert(node->_next == NULL, "node already in list");
// put node at end of queue (circular doubly linked list)
if (_WaitSet == NULL) {
_WaitSet = node;
node->_prev = node;
node->_next = node;
} else {
ObjectWaiter* head = _WaitSet ; // 通过双向链表的方式,将ObjectWaiter对象添加到_WaitSet列表中
ObjectWaiter* tail = head->_prev;
assert(tail->_next == head, "invariant check");
tail->_next = node;
head->_prev = node;
node->_next = head;
node->_prev = tail;
}
}
查看notify方法源码:
void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
TEVENT (Empty-Notify) ;// _WaitSet=NULL表明没有等待状态的线程,直接返回。
return ;
}
DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
int Policy = Knob_MoveNotifyee ;
Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;
ObjectWaiter * iterator = DequeueWaiter() ;// 获取一个ObjectWaiter对象
if (iterator != NULL) {
...
ObjectWaiter * List = _EntryList ;
if (List != NULL) {
assert (List->_prev == NULL, "invariant") ;
assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
assert (List != iterator, "invariant") ;
}
// 根据不同状态采取不同策略,将从_WaitSet列表中移出来的ObjectWaiter对象加入到_EntryList列表中。
if (Policy == 0) { // prepend to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
List->_prev = iterator ;
iterator->_next = List ;
iterator->_prev = NULL ;
_EntryList = iterator ;
}
} else
if (Policy == 1) {...} else // append to EntryList
if (Policy == 2) {...} else // prepend to cxq
if (Policy == 3) { // append to cxq
...
} else {
ParkEvent * ev = iterator->_event ;
iterator->TState = ObjectWaiter::TS_RUN ;
OrderAccess::fence() ; // 被唤醒的线程又变成run状态。
ev->unpark() ;
}
}
查看notifyAll方法源码:
void ObjectMonitor::notifyAll(TRAPS) {
CHECK_OWNER();
ObjectWaiter* iterator;
if (_WaitSet == NULL) {
TEVENT (Empty-NotifyAll) ;// _WaitSet=NULL表明没有等待状态的线程,直接返回。
return ;
}
DTRACE_MONITOR_PROBE(notifyAll, this, object(), THREAD);
int Policy = Knob_MoveNotifyee ;
int Tally = 0 ;
Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notifyall") ;
for (;;) {
iterator = DequeueWaiter () ;// 循环获取所以ObjectWaiter对象
...
ObjectWaiter * List = _EntryList ;
if (List != NULL) {
assert (List->_prev == NULL, "invariant") ;
assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
assert (List != iterator, "invariant") ;
}
// 根据不同状态采取不同策略,将从_WaitSet列表中移出来的ObjectWaiter对象加入到_EntryList列表中。
if (Policy == 0) { // prepend to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
List->_prev = iterator ;
iterator->_next = List ;
iterator->_prev = NULL ;
_EntryList = iterator ;
}
} else
if (Policy == 1) { // append to EntryList
...
} else
if (Policy == 2) { // prepend to cxq
...
} else
if (Policy == 3) { // append to cxq
...
} else {
ParkEvent * ev = iterator->_event ;
iterator->TState = ObjectWaiter::TS_RUN ;// 被唤醒的线程又变成run状态。
OrderAccess::fence() ;
ev->unpark() ;
}
...
可见,wait()与notify()/notifyAll()的实现都跟Monitor有很大关联。
- 当多线程访问一段同步代码块时,这些都线程会被被封装成一个个ObjectWatier对象,并被放入 _EntryList列表中,也就是被放到 Entry Set(入口区) 中等待获取锁。
- 如果该线程获取到了锁(acquire),线程就会成为当前锁的 Owner。
- 获取到锁的线程可也以通过调用 wait 方法将锁释放(release),然后该线程对象会被放入_WaitSet列表中,进入Wait Set (等待区)进行等待(阻塞BLOCKED)。
- 当获取到锁的对象调用notify/notifyAll方法唤醒等待区被阻塞的线程时,线程重新竞争锁。如果竞争锁成功,那么线程就进入RUNNABLE状态;如果竞争锁失败,这些线程会重新进入到Entry Set区再重新去竞争锁。
wait方法的使用对应上图的第3步,也就是说,调用wait()
、notify()
/notifyAll()
方法的对象必须已经获取到锁。
如何确保调用对象获取到锁呢?使用sychronized关键字呗!所以说这些方法调用也必须发生在sychronized修饰的同步代码块内。
2.2 等待/通知机制
(1)什么是等待/通知机制
等待/通知机制是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去竞争锁。但这并不是故事的全部,线程间也有协作机制。就好比我们在公司中与同事关系,可能存在在晋升时的竞争,但更多时候是一起合作以完成某些任务。
wait/notify 就是线程间的一种协作机制。
当一个线程调用wait()/wait(long)方法后,进入WAITING状态或者TIMED_WAITING状态(阻塞),并释放锁与CPU资源。只有其他获取到锁的线程执行完他们的指定代码过后,再通过notify()方法将其唤醒。 如果需要,也可以使用notifyAll()
来唤醒所有的阻塞线程。
(2)等待/通知使用方法
等待/通知机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:
- wait:线程不再活动,不再参与调度,释放它对锁的拥有权。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从WAITING状态中释放出来,重新进入到调度队列(ready queue)中。
- notify:唤醒一个等待当前对象的锁的线程。唤醒在此对象监视器上等待的单个线程。
- notifyAll:唤醒在此对象监视器上等待的所有线程。
注意:
哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以它需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行。
总结如下:
- 如果能获取锁,线程就从 WAITING/TIMED_WAITING 状态转换为RUNNABLE 状态;
- 否则,从 Wait Set 区出来,又进入 Entry Set区,线程就从 WAITING 状态又变成 BLOCKED 状态。
(3)调用wait和notify方法需要注意的细节
- wait方法与notify方法必须要由同一个锁对象调用。因为对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
- wait方法与notify方法是属于Object类的方法的。因为锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
- wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为必须要通过锁对象调用这2个方法。