摘要:在進(jìn)入狀態(tài)前,線程會(huì)將持有的鎖先釋放掉。被喚醒后的線程,拿不到鎖的線程將進(jìn)入狀態(tài),直到它們拿到鎖為止。在等待之后測(cè)試條件,如果條件不成立的話繼續(xù)等待,這對(duì)于確保安全性是必要的。
WAITING(TIMED_WAITING) 與 BLOCKED
看《Java特種兵》的時(shí)候發(fā)現(xiàn),Thread.join可以使線程進(jìn)入WAITING狀態(tài),再結(jié)合姊妹篇線程的狀態(tài)我們可以了解到,有兩個(gè)類狀態(tài)非常接近:WAITING(TIMED_WAITING) 與 BLOCKED,這兩者都會(huì)讓線程看上去“阻塞”在某處了。
什么時(shí)候線程會(huì)進(jìn)入WAITING(無限期等待)的狀態(tài)中呢?常用的有兩個(gè),分別是①Object.wait without timeout,②Thread.join without timeout【另外還有③LockSupport的park方法,④Conditon的await方法】;TIMED_WAITING除了①Object.wait with timeout、②Thread.join with timeout,還需要添加一條③Thread.sleep方法【另外還有④LockSupport的parkNanos方法,帶有時(shí)間】。
在進(jìn)入WAITING狀態(tài)前,線程會(huì)將持有的鎖先釋放掉。WAITING狀態(tài)中的線程需要被其他線程對(duì)同一個(gè)對(duì)象調(diào)用notify()或notifyAll()方法才能喚醒。被notifyAll()喚醒后的線程,拿不到鎖的線程將進(jìn)入BLOCKED狀態(tài),直到它們拿到鎖為止。簡(jiǎn)而言之,WAITING類狀態(tài)中的線程和BLOCKED狀態(tài)中的線程的區(qū)別在于:WAITING狀態(tài)的線程需要被其他線程喚醒;BLOCKED中的線程,需要等待其他線程釋放鎖,此處的鎖特指synchronized塊。
見下圖
Join為什么會(huì)使線程進(jìn)入WAITING(TIMED_WAITING) 狀態(tài)中呢?我們看一下底層代碼
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; ? if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } ? if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
從12、20行中可以看出,join方法底層調(diào)用的就是wait方法。
sleep與wait同樣會(huì)使線程進(jìn)入TIMED_WAITING狀態(tài)中的sleep和wait方法,它們之間有什么區(qū)別呢?
wait()wait()方法釋放鎖
wait()是Object類的方法
wait()是非靜態(tài)方法
public final void wait() throws InterruptedException { //...}
wait()應(yīng)該被notify()或者notifyAll()方法喚醒
wait()方法需要在循環(huán)中調(diào)用(推薦不強(qiáng)制)
waint()方法必須放在synchronized上下文(synchronized方法或代碼塊)中,不然會(huì)拋出IllegalMonitorStateException
sleep()sleep()方法不會(huì)釋放鎖
sleep()是java.lang.Thread類的方法
sleep()是靜態(tài)方法
public static void sleep(long millis, int nanos) throws InterruptedException { //... }
sleep()方法是在特定時(shí)間后結(jié)束
sleep()方法最好不在放在循環(huán)中
sleep()方法可以在任意地方執(zhí)行
wait為什么要放在循環(huán)中?synchronized(obj) { while (!condition) { obj.wait(); } }
這里引用一段《Effective Java》
始終應(yīng)該使用wait循環(huán)模式來調(diào)用wait方法;永遠(yuǎn)不要在循環(huán)之外調(diào)用wait方法。循環(huán)會(huì)在等待之前和之后測(cè)試條件。
在等待之前測(cè)試條件,當(dāng)條件已經(jīng)成立時(shí)就跳過等待,這對(duì)于確保活性(liveness)是必要的。如果條件已經(jīng)成立,并且在線程等待之前,notify (或者notifyAll)方法已經(jīng)被調(diào)用, 則無法保證該線程將會(huì)從等待中蘇醒過來。
在等待之后測(cè)試條件,如果條件不成立的話繼續(xù)等待,這對(duì)于確保安全性(safety)是必要的。當(dāng)條件不成立的時(shí)候,如果線程繼續(xù)執(zhí)行,則可能會(huì)破壞被鎖保護(hù)的約束關(guān)系。當(dāng)條件不成立時(shí),有下面一些理由可使一個(gè)線程蘇醒過來:
- 另一個(gè)線程可能已經(jīng)得到了鎖,并且從一個(gè)線程調(diào)用notify那一刻起,到等待線程蘇醒過來的這段時(shí)間中,得到鎖的線程已經(jīng)改變了受保護(hù)的狀態(tài)。 - 條件并不成立,但是另一個(gè)線程可能意外地或惡意地調(diào)用了 notify。在公有可訪問的對(duì)象上等待,這些類實(shí)際上把自己暴露在了這種危險(xiǎn)的境地中。公有可訪問對(duì)象的同步方法中包含的wait都會(huì)出現(xiàn)這樣知問題。 - 通知線程(notifying thread)在喚醒等待線程時(shí)可能會(huì)過度“大方”。例如,即使只有某一些等待線程的條件已經(jīng)被滿足,但是通知線程可能仍然調(diào)用notifyAll。 - 在沒有通知的情況下,等待線程也可能(但很少)會(huì)蘇醒過來。這被稱為“偽喚醒 (spurious wakeup)”
我們針對(duì)【跳過等待】和【繼續(xù)等待】舉個(gè)形象的例子:
針對(duì)【前置判斷,跳過等待】:如果是兩個(gè)狙擊手,在同時(shí)等待一個(gè)人(鎖),判斷條件是這個(gè)人還活著。如果沒有前置的判斷,在等待前不校驗(yàn)這個(gè)人是否活著,那么當(dāng)狙擊手甲殺死目標(biāo)并通知狙擊手乙之后,乙才進(jìn)入等待狀態(tài),那么乙將會(huì)死等一個(gè)已經(jīng)被殺死的目標(biāo),乙將失去活性(liveness)。
針對(duì)【后置判斷,繼續(xù)等待】:還是兩個(gè)狙擊手,如果他們被喚醒后,沒有后置校驗(yàn),那么將導(dǎo)致可笑的錯(cuò)誤,比如狙擊手甲已經(jīng)將目標(biāo)殺死了,狙擊手乙被喚醒后,沒有再校驗(yàn)條件,直接開槍殺人,將會(huì)殺死目標(biāo)兩次。如果是冪等的還則罷了,不冪等的將導(dǎo)致錯(cuò)誤。
綜上所述,wait前后都要校驗(yàn),而最好的辦法就是循環(huán)。
Thread.join后誰在WAITING?從上文中我們已經(jīng)知道了,join可以使線程處于WAITING狀態(tài),那問題來了,是子線程處于WAITING狀態(tài)還是父線程處于WAITING狀態(tài)?我們做個(gè)小試驗(yàn):
public class TestJoin { public static void main(String[] args) throws InterruptedException { Thread.currentThread().setName("TestJoin main...."); Thread joinThread = new Thread(new Runnable() { @Override public void run() { for (; ; ) { ? } } }, "join thread"); ? joinThread.start(); joinThread.join(); } }
按照在線程的狀態(tài)中提供的方法,我們可以得到:
子線程即join的線程依舊是RUNNABLE狀態(tài)
"join thread" #10 prio=5 os_prio=31 tid=0x00007fca1b801000 nid=0x5503 runnable [0x0000700001725000]
java.lang.Thread.State: RUNNABLE
at com.meituan.java8.thread.TestJoin$1.run(TestJoin.java:13) at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
父線程(在此例中是主線程)為WAITING狀態(tài)
"TestJoin main...." #1 prio=5 os_prio=31 tid=0x00007fca1c003000 nid=0x1903 in Object.wait() [0x00007000003ec000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076ad94fa0> (a java.lang.Thread) at java.lang.Thread.join(Thread.java:1252) - locked <0x000000076ad94fa0> (a java.lang.Thread) at java.lang.Thread.join(Thread.java:1326) at com.meituan.java8.thread.TestJoin.main(TestJoin.java:20) Locked ownable synchronizers: - None
我們對(duì)代碼做稍稍改動(dòng),可以驗(yàn)證sleep后的線程在什么狀態(tài)
Thread joinThread = new Thread(new Runnable() { @Override public void run() { try { TimeUnit.MINUTES.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } } }, "sleeping thread");
"sleeping thread" #10 prio=5 os_prio=31 tid=0x00007f92620bc000 nid=0x5503 waiting on condition [0x00007000077b7000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.meituan.java8.thread.TestJoin$1.run(TestJoin.java:16) at java.lang.Thread.run(Thread.java:748) Locked ownable synchronizers: - None參考文檔
https://javaconceptoftheday.c...
A thread enters into WAITING state when it calls wait() or join() method on an object. Before entering into WAITING state, thread releases the lock of the object it holds. It will remain in WAITING state until any other thread calls either notify() or notifyAll() on the same object.
Once the other thread calls notify() or notifyAll() on the same object, one or all the threads which are WAITING for lock of that object will be notified. All the notified threads will not get the object lock immediately. They will get the object lock on a priority basis once the current thread releases the lock. Until that they will be in BLOCKED state.
In simple terms, a thread will be in WAITING state if it is waiting for notification from other threads. A thread will be in BLOCKED state if it is waiting for other thread to release the lock it wants.
https://stackoverflow.com/que...
The difference is relatively simple.
In the BLOCKED state, a thread is about to enter a synchronized block, but there is another thread currently running inside a synchronized block on the same object. The first thread must then wait for the second thread to exit its block.
In the WAITING state, a thread is waiting for a signal from another thread. This happens typically by calling Object.wait(), or Thread.join(). The thread will then remain in this state until another thread calls Object.notify(), or dies.
https://stackoverflow.com/a/3...
wait()
wait() method releases the lock.
wait() is the method of Object class.
wait() is the non-static method - public final void wait() throws InterruptedException { //...}
wait() should be notified by notify() or notifyAll() methods.
wait() method needs to be called from a loop in order to deal with false alarm.
wait() method must be called from synchronized context (i.e. synchronized method or block), otherwise it will throw IllegalMonitorStateException
sleep()
sleep() method doesn"t release the lock.
sleep() is the method of java.lang.Thread class.
sleep() is the static method - public static void sleep(long millis, int nanos) throws InterruptedException { //... }
after the specified amount of time, sleep() is completed.
sleep() better not to call from loop(i.e. see code below).
sleep() may be called from anywhere. there is no specific requirement.
4.《Effective Java》第10章
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/73928.html
摘要:有這么兩段小程序。毫無疑問,把這兩段小程序貼到瀏覽器里跑一下,能很快地得到答案。而在第二段小程序中,由于在中,表達(dá)式的值在運(yùn)行之前將會(huì)被轉(zhuǎn)化,將會(huì)把表達(dá)式和聲明提升到當(dāng)前作用域的頂部。兩段小程序,考考作用域和變量聲明提升,怎么樣,答對(duì)了么 有這么兩段小程序。 var goo = hello; function foo(){ if(true){ goo = world; ...
摘要:如下面的例子,在學(xué)習(xí)線程時(shí),將文件名命名為腳本完全正常沒問題,結(jié)果報(bào)下面的錯(cuò)誤。最大的問題就是的多線程程序并不能利用多核的優(yōu)勢(shì)比如一個(gè)使用了多個(gè)線程的計(jì)算密集型程序只會(huì)在一個(gè)單上面運(yùn)行。 本文記錄學(xué)習(xí)Python遇到的問題和一些常用用法,注本開發(fā)環(huán)境的Python版本為2.7。 一、python文件命名 在python文件命名時(shí),一定要注意不能和系統(tǒng)默認(rèn)的模塊名沖突,否則會(huì)報(bào)錯(cuò)。如下面...
摘要:如果線程還存活,線程就無限期等待,并讓出監(jiān)視器鎖,進(jìn)入狀態(tài)。當(dāng)線程從狀態(tài)被喚醒后通過,或者是假喚醒將繼續(xù)競(jìng)爭(zhēng)監(jiān)視器鎖,當(dāng)成功獲得監(jiān)視器鎖后,他將從調(diào)用的地方恢復(fù),繼續(xù)運(yùn)行。 前言 系列文章目錄 上一篇我們討論了線程的創(chuàng)建,本篇我們來聊一聊線程的狀態(tài)轉(zhuǎn)換以及常用的幾個(gè)比較重要的方法。 本篇依然是通過源碼分析來了解這些知識(shí)。 本文源碼基于jdk1.8 。 閱讀完本文,你應(yīng)當(dāng)有能力回答以...
摘要:如果某線程并未使用很多操作,它會(huì)在自己的時(shí)間片內(nèi)一直占用處理器和。在中使用線程在和等大多數(shù)類系統(tǒng)上運(yùn)行時(shí),支持多線程編程。守護(hù)線程另一個(gè)避免使用模塊的原因是,它不支持守護(hù)線程。 這一篇是Python并發(fā)的第四篇,主要介紹進(jìn)程和線程的定義,Python線程和全局解釋器鎖以及Python如何使用thread模塊處理并發(fā) 引言&動(dòng)機(jī) 考慮一下這個(gè)場(chǎng)景,我們有10000條數(shù)據(jù)需要處理,處理每條...
閱讀 1767·2023-04-26 01:41
閱讀 3073·2021-11-23 09:51
閱讀 2733·2021-10-09 09:43
閱讀 9019·2021-09-22 15:13
閱讀 2452·2021-09-07 09:59
閱讀 2624·2019-08-30 15:44
閱讀 1132·2019-08-30 12:45
閱讀 2618·2019-08-30 12:43