thread.sleep是让哪个线程休眠_Java多线程:多线程基础知识
本文來源:https://www.cnblogs.com/ITtangtang/p/7602363.html
一、線程安全性
定義:多個(gè)線程之間的操作無論采用何種執(zhí)行時(shí)序或交替方式,都要保證不變性條件不被破壞
“共享”:變量可以由多個(gè)線程同時(shí)訪問;
“可變”:變量的值在其生命周期內(nèi)可以發(fā)生改變
如果當(dāng)多個(gè)線程訪問同一個(gè)可變的狀態(tài)變量時(shí),沒有使用合適的同步,那么程序?qū)?huì)出現(xiàn)錯(cuò)誤。有三種方式可以修復(fù)該問題:
不在線程之間共享該變量。
將狀態(tài)變量修改為不可變的變量。
在訪問狀態(tài)變量時(shí)使用同步。
競(jìng)態(tài)條件:
最常見的類型就是“先檢查后執(zhí)行”操作,即通過一個(gè)可能失效的觀測(cè)接口來決定下一步的動(dòng)作。
二、線程啟動(dòng)
繼承Thread類,重寫里面的run方法,用start方法啟動(dòng)線程
實(shí)現(xiàn)Runnable接口,實(shí)現(xiàn)里面的run方法,用new Thread(Runnable target).start()方法來啟動(dòng)
start()和run()
線程的啟動(dòng)并不是簡(jiǎn)單的調(diào)用了run方法,而是由一個(gè)線程調(diào)度器來分別調(diào)用所有線程的run方法,普通的run方法如果沒有執(zhí)行完是不會(huì)返回的,也就是會(huì)一直執(zhí)行下去,這樣run方法下面的方法就不可能會(huì)執(zhí)行了,可是線程里的run方法卻不一樣,它只有一定的CPU時(shí)間,執(zhí)行過后就給別的線程了,這樣反復(fù)的把CPU的時(shí)間切來切去,因?yàn)榍袚Q的速度很快,所以我們就感覺是很多線程在同時(shí)運(yùn)行一樣
(01) mythread.run()是在“主線程main”中調(diào)用的,該run()方法直接運(yùn)行在“主線程main”上。
(02) mythread.start()會(huì)啟動(dòng)“線程mythread”,“線程mythread”啟動(dòng)之后,會(huì)調(diào)用run()方法;此時(shí)的run()方法是運(yùn)行在“線程mythread”上。
http://blog.csdn.net/xuxurui007/article/details/7685076
三、線程同步
synchronized關(guān)鍵字
內(nèi)置鎖(互斥鎖):
在java中,每一個(gè)對(duì)象有且僅有一個(gè)同步鎖。這也意味著,同步鎖是依賴于對(duì)象而存在。線程在進(jìn)入同步代碼塊之前會(huì)自動(dòng)獲得鎖,并且在退出同步代碼塊時(shí)自動(dòng)釋放鎖。同一時(shí)間最多只有一個(gè)線程持有該鎖,其他線程必須等待或者阻塞,直到該線程釋放鎖。
如果持有鎖的時(shí)間過長,將會(huì)帶來性能問題。
實(shí)例鎖 -- 鎖在某一個(gè)實(shí)例對(duì)象上。如果該類是單例,那么該鎖也具有全局鎖的概念。
實(shí)例鎖對(duì)應(yīng)的就是synchronized關(guān)鍵字。
全局鎖 -- 該鎖針對(duì)的是類,無論實(shí)例多少個(gè)對(duì)象,那么線程都共享該鎖。
全局鎖對(duì)應(yīng)的就是static synchronized(或者是鎖在該類的class或者classloader對(duì)象上)。
volatile變量
volatile相當(dāng)于synchronized的弱實(shí)現(xiàn),也就是說volatile實(shí)現(xiàn)了類似synchronized的語義,卻又沒有鎖機(jī)制。它確保對(duì)volatile字段的更新以可預(yù)見的方式告知其他的線程。
volatile包含以下語義:
(1)Java 存儲(chǔ)模型不會(huì)對(duì)valatile指令的操作進(jìn)行重排序:這個(gè)保證對(duì)volatile變量的操作時(shí)按照指令的出現(xiàn)順序執(zhí)行的。
(2)volatile變量不會(huì)被緩存在寄存器中(只有擁有線程可見)或者其他對(duì)CPU不可見的地方,每次總是從主存中讀取volatile變量的結(jié)果。也就是說對(duì)于volatile變量的修改,其它線程總是可見的,并且不是使用自己線程棧內(nèi)部的變量。也就是在happens-before法則中,對(duì)一個(gè)valatile變量的寫操作后,其后的任何讀操作理解可見此寫操作的結(jié)果。
盡管volatile變量的特性不錯(cuò),但是volatile并不能保證線程安全的,也就是說volatile字段的操作不是原子性的,volatile變量只能保證可見性(一個(gè)線程修改后其它線程能夠理解看到此變化后的結(jié)果),要想保證原子性,目前為止只能加鎖!
四、線程協(xié)作
線程等待:
等待的原因可能是如下幾種情況:
(1)sleep() 的作用是讓當(dāng)前線程休眠,即當(dāng)前線程會(huì)從“運(yùn)行狀態(tài)”進(jìn)入到“休眠(阻塞)狀態(tài)”。sleep()會(huì)指定休眠時(shí)間,線程休眠的時(shí)間會(huì)大于/等于該休眠時(shí)間;在線程重新被喚醒時(shí),它會(huì)由“阻塞狀態(tài)”變成“就緒狀態(tài)”,從而等待cpu的調(diào)度執(zhí)行。
(2)通過調(diào)用join()方法使線程掛起,使自己等待另一個(gè)線程的結(jié)果,直到另一個(gè)線程執(zhí)行完畢為止。
(3)通過調(diào)用wait()方法使線程掛起,直到線程得到了notify()和notifyAll()消息,線程才會(huì)進(jìn)入“可執(zhí)行”狀態(tài)。
(4)yield()的作用是讓步。它能讓當(dāng)前線程由“運(yùn)行狀態(tài)”進(jìn)入到“就緒狀態(tài)”,從而讓其它具有相同優(yōu)先級(jí)的等待線程獲取執(zhí)行權(quán);但是,并不能保證在當(dāng)前線程調(diào)用yield()之后,其它具有相同優(yōu)先級(jí)的線程就一定能獲得執(zhí)行權(quán);也有可能是當(dāng)前線程又進(jìn)入到“運(yùn)行狀態(tài)”繼續(xù)運(yùn)行!
注意:
(01) wait()是讓線程由“運(yùn)行狀態(tài)”進(jìn)入到“等待(阻塞)狀態(tài)”,而yield()是讓線程由“運(yùn)行狀態(tài)”進(jìn)入到“就緒狀態(tài)”。
(02) wait()是會(huì)讓線程釋放它所持有對(duì)象的同步鎖,而yield()方法不會(huì)釋放鎖。
(03) sleep()使當(dāng)前線程暫停執(zhí)行一段時(shí)間,從而讓其他線程有機(jī)會(huì)繼續(xù)執(zhí)行,但它并不釋放對(duì)象鎖
wait():
“當(dāng)前線程”在調(diào)用wait()時(shí),必須擁有該對(duì)象的同步鎖。該線程調(diào)用wait()之后,會(huì)釋放該鎖;然后一直等待直到“其它線程”調(diào)用對(duì)象的同步鎖的notify()或notifyAll()方法。然后,該線程繼續(xù)等待直到它重新獲取“該對(duì)象的同步鎖”,然后就可以接著運(yùn)行wait()后面的代碼。wait()的作用是讓“當(dāng)前線程”等待,而“當(dāng)前線程”是指正在cpu上運(yùn)行的線程!因此調(diào)用wait()方法必須在同步塊或者同步方法中進(jìn)行(synchronized塊或者synchronized方法)
wait的東西一定要notify嗎?不一定,
notify():
notify() 執(zhí)行該方法的線程喚醒在對(duì)象的等待池中等待的一個(gè)線程,JVM從對(duì)象的等待池中隨機(jī)選擇一個(gè)線程,把它轉(zhuǎn)到對(duì)象的鎖池中。使線程由阻塞隊(duì)列進(jìn)入就緒狀態(tài)。
notifyAll():
當(dāng)前的線程已經(jīng)放棄對(duì)資源的占有,通知所有的等待線程從wait()方法后的語句開始運(yùn)行。
這里要注意一點(diǎn):notify()和notifyAll()方法只是喚醒等待該對(duì)象的鎖的線程,并不決定哪個(gè)線程能夠獲取到鎖.
舉個(gè)簡(jiǎn)單的例子:
假如有三個(gè)線程Thread1、Thread2和Thread3都在等待對(duì)象objectA的鎖,此時(shí)Thread4擁有對(duì)象objectA的鎖,當(dāng)在Thread4中調(diào)用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一個(gè)能被喚醒。注意,被喚醒不等于立刻就獲取了objectA的鎖,假若在Thread4中調(diào)用objectA.notifyAll()方法,則Thread1、Thread2和Thread3三個(gè)線程都會(huì)被喚醒,至于哪個(gè)線程接下來能夠獲取到objectA的鎖就具體依賴于操作系統(tǒng)的調(diào)度了。
上面尤其要注意一點(diǎn),一個(gè)線程被喚醒不代表立即獲取了對(duì)象的鎖,只有等調(diào)用完notify()或者notifyAll()并退出synchronized塊,釋放對(duì)象鎖后,其余線程才可獲得鎖執(zhí)行。
首先,wait()和notify(),notifyAll()是Object類的方法,sleep()和yield()是Thread類的方法。
當(dāng)然由于Thread類繼承了Object類,所以Thread也可以調(diào)用前面三個(gè)方法
由于每個(gè)對(duì)象都擁有鎖,所以讓當(dāng)前線程等待某個(gè)對(duì)象的鎖,當(dāng)然應(yīng)該通過這個(gè)對(duì)象來操作了。而不是用當(dāng)前線程來操作,因?yàn)楫?dāng)前線程可能會(huì)等待多個(gè)對(duì)象的鎖,如果通過線程來操作,就非常復(fù)雜了。
(1).常用的wait方法有wait()和wait(long timeout):
void wait() 在其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法前,導(dǎo)致當(dāng)前線程等待。
void wait(long timeout) 在其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法,或者超過指定的時(shí)間量前,導(dǎo)致當(dāng)前線程等待。
wait()后,線程會(huì)釋放掉它所占有的“鎖標(biāo)志”,從而使線程所在對(duì)象中的其它synchronized數(shù)據(jù)可被別的線程使用。
wait()和notify()因?yàn)闀?huì)對(duì)對(duì)象的“鎖標(biāo)志”進(jìn)行操作,所以它們必須在synchronized函數(shù)或synchronized block中進(jìn)行調(diào)用。如果在non-synchronized函數(shù)或non-synchronized block中進(jìn)行調(diào)用,雖然能編譯通過,但在運(yùn) 行時(shí)會(huì)發(fā)生IllegalMonitorStateException的異常。
(2).Thread.sleep(long millis),必須帶有一個(gè)時(shí)間參數(shù)。
sleep(long)使當(dāng)前線程進(jìn)入停滯狀態(tài),所以執(zhí)行sleep()的線程在指定的時(shí)間內(nèi)肯定不會(huì)被執(zhí)行;
sleep(long)可使優(yōu)先級(jí)低的線程得到執(zhí)行的機(jī)會(huì),當(dāng)然也可以讓同優(yōu)先級(jí)和高優(yōu)先級(jí)的線程有執(zhí)行的機(jī)會(huì);
sleep(long)是不會(huì)釋放鎖標(biāo)志的。
(3).yield()沒有參數(shù)。
sleep 方法使當(dāng)前運(yùn)行中的線程睡眼一段時(shí)間,進(jìn)入不可運(yùn)行狀態(tài),這段時(shí)間的長短是由程序設(shè)定的,yield 方法使當(dāng)前線程讓出CPU占有權(quán),但讓出的時(shí)間是不可設(shè)定的。
yield()也不會(huì)釋放鎖標(biāo)志。
實(shí)際上,yield()方法對(duì)應(yīng)了如下操作:先檢測(cè)當(dāng)前是否有相同優(yōu)先級(jí)的線程處于同可運(yùn)行狀態(tài),如有,則把 CPU 的占有權(quán)交給此線程,否則繼續(xù)運(yùn)行原來的線程。所以yield()方法稱為“退讓”,它把運(yùn)行機(jī)會(huì)讓給了同等優(yōu)先級(jí)的其他線程。
sleep方法允許較低優(yōu)先級(jí)的線程獲得運(yùn)行機(jī)會(huì),但yield()方法執(zhí)行時(shí),當(dāng)前線程仍處在可運(yùn)行狀態(tài),所以不可能讓出較低優(yōu)先級(jí)的線程些時(shí)獲得CPU占有權(quán)。在一個(gè)運(yùn)行系統(tǒng)中,如果較高優(yōu)先級(jí)的線程沒有調(diào)用 sleep 方法,又沒有受到 I/O阻塞,那么較低優(yōu)先級(jí)線程只能等待所有較高優(yōu)先級(jí)的線程運(yùn)行結(jié)束,才有機(jī)會(huì)運(yùn)行。
yield()只是使當(dāng)前線程重新回到可執(zhí)行狀態(tài),所以執(zhí)行yield()的線程有可能在進(jìn)入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行。所以yield()只能使同優(yōu)先級(jí)的線程有執(zhí)行的機(jī)會(huì)。
sleep和yield區(qū)別:
1、sleep()方法會(huì)給其他線程運(yùn)行的機(jī)會(huì),而不考慮其他線程的優(yōu)先級(jí),因此會(huì)給較低線程一個(gè)運(yùn)行的機(jī)會(huì);yield()方法只會(huì)給相同優(yōu)先級(jí)或者更高優(yōu)先級(jí)的線程一個(gè)運(yùn)行的機(jī)會(huì)。
2、當(dāng)線程執(zhí)行了sleep(long millis)方法后,將轉(zhuǎn)到阻塞狀態(tài),參數(shù)millis指定睡眠時(shí)間;當(dāng)線程執(zhí)行了yield()方法后,將轉(zhuǎn)到就緒狀態(tài)。
3、sleep()方法聲明拋出InterruptedException異常,而yield()方法沒有聲明拋出任何異常
為什么notify(), wait()等函數(shù)定義在Object中,而不是Thread中?
Object中的wait(), notify()等函數(shù),和synchronized一樣,會(huì)對(duì)“對(duì)象的同步鎖”進(jìn)行操作。
wait()會(huì)使“當(dāng)前線程”等待,因?yàn)榫€程進(jìn)入等待狀態(tài),所以線程應(yīng)該釋放它鎖持有的“同步鎖”,否則其它線程獲取不到該“同步鎖”而無法運(yùn)行!那么:notify()是依據(jù)什么喚醒等待線程的?或者說,wait()等待線程和notify()之間是通過什么關(guān)聯(lián)起來的?答案是:依據(jù)“對(duì)象的同步鎖”。
負(fù)責(zé)喚醒等待線程的那個(gè)線程(我們稱為“喚醒線程”),它只有在獲取“該對(duì)象的同步鎖”(這里的同步鎖必須和等待線程的同步鎖是同一個(gè)),并且調(diào)用notify()或notifyAll()方法之后,才能喚醒等待線程。雖然,等待線程被喚醒;但是,它不能立刻執(zhí)行,因?yàn)閱拘丫€程還持有“該對(duì)象的同步鎖”。必須等到喚醒線程釋放了“對(duì)象的同步鎖”之后,等待線程才能獲取到“對(duì)象的同步鎖”進(jìn)而繼續(xù)運(yùn)行。
總之,notify(), wait()依賴于“同步鎖”,而“同步鎖”是對(duì)象鎖持有,并且每個(gè)對(duì)象有且僅有一個(gè)!這就是為什么notify(), wait()等函數(shù)定義在Object類,而不是Thread類中的原因。
Condition
Condition是在java 1.5中才出現(xiàn)的,它用來替代傳統(tǒng)的Object的wait()、notify()實(shí)現(xiàn)線程間的協(xié)作,相比使用Object的wait()、notify(),使用Condition的await()、signal()這種方式實(shí)現(xiàn)線程間協(xié)作更加安全和高效。因此通常來說比較推薦使用Condition,Java阻塞隊(duì)列實(shí)際上是使用了Condition來模擬線程間協(xié)作。
Condition是個(gè)接口,基本的方法就是await()和signal()方法;
Condition依賴于Lock接口,生成一個(gè)Condition的基本代碼是lock.newCondition()
調(diào)用Condition的await()和signal()方法,都必須在lock保護(hù)之內(nèi),就是說必須在lock.lock()和lock.unlock之間才可以使用
Conditon中的await()對(duì)應(yīng)Object的wait();
Condition中的signal()對(duì)應(yīng)Object的notify();
Condition中的signalAll()對(duì)應(yīng)Object的notifyAll()。
五、線程中斷
http://blog.csdn.net/dlite/article/details/4218105
Java的線程調(diào)度不提供搶占式中斷,而采用協(xié)作式的中斷。其實(shí),協(xié)作式的中斷,原理很簡(jiǎn)單,就是輪詢某個(gè)表示中斷的標(biāo)記。
通常,我們通過“標(biāo)記”方式終止處于“運(yùn)行狀態(tài)”的線程。其中,包括“中斷標(biāo)記”和“額外添加標(biāo)記”。
(01) 通過“中斷標(biāo)記”終止線程。
形式如下:
1@Override2publicvoid?run()?{
3????while?(!isInterrupted())?{
4????????//?執(zhí)行任務(wù)...????}
5}
說明:isInterrupted()是判斷線程的中斷標(biāo)記是不是為true。當(dāng)線程處于運(yùn)行狀態(tài),并且我們需要終止它時(shí);可以調(diào)用線程的interrupt()方法,使用線程的中斷標(biāo)記為true,即isInterrupted()會(huì)返回true。此時(shí),就會(huì)退出while循環(huán)。
注意:interrupt()并不會(huì)終止處于“運(yùn)行狀態(tài)”的線程!它會(huì)將線程的中斷標(biāo)記設(shè)為true。
(02) 通過“額外添加標(biāo)記”。
形式如下:
1private?volatile?boolean?flag?=?true;2protected?void?stopTask()?{
3????flag?=?false;
4}
5
6@Override
7public?void?run()?{
8????while?(flag)?{
9????????//?執(zhí)行任務(wù)...????}
10}
說明:線程中有一個(gè)flag標(biāo)記,它的默認(rèn)值是true;并且我們提供stopTask()來設(shè)置flag標(biāo)記。當(dāng)我們需要終止該線程時(shí),調(diào)用該線程的stopTask()方法就可以讓線程退出while循環(huán)。
注意:將flag定義為volatile類型,是為了保證flag的可見性。即其它線程通過stopTask()修改了flag之后,本線程能看到修改后的flag的值。
通常,我們通過“中斷”方式終止處于“阻塞狀態(tài)”的線程。
當(dāng)線程由于被調(diào)用了sleep(), wait(), join()等方法而進(jìn)入阻塞狀態(tài);若此時(shí)調(diào)用線程的interrupt()將線程的中斷標(biāo)記設(shè)為true。由于處于阻塞狀態(tài),中斷標(biāo)記會(huì)被清除,同時(shí)產(chǎn)生一個(gè)InterruptedException異常。將InterruptedException放在適當(dāng)?shù)臑橹咕湍芙K止線程
1@Override2public?void?run()?{
3????try?{
4????????while?(true)?{
5????????????//?執(zhí)行任務(wù)...????????}
6????}?catch?(InterruptedException?ie)?{??
7????????//?由于產(chǎn)生InterruptedException異常,退出while(true)循環(huán),線程終止!????}
8}
注意:對(duì)InterruptedException的捕獲務(wù)一般放在while(true)循環(huán)體的外面,這樣,在產(chǎn)生異常時(shí)就退出了while(true)循環(huán)。
參考資料:
http://www.cnblogs.com/jobs/archive/2010/07/29/1788156.html
梁飛:Java并發(fā)編程常識(shí).pptx
●?Kafka基本架構(gòu)及原理
●?JVM:基礎(chǔ)
●?數(shù)據(jù)倉庫:Mysql大量數(shù)據(jù)快速導(dǎo)出
●?深入理解Java:注解
●?深入理解Java:類加載機(jī)制及反射
●?Java 如何設(shè)計(jì) API 接口,實(shí)現(xiàn)統(tǒng)一格式返回?
●?圖文:你女朋友也能看懂的 Kubernetes !
●?打開黑盒:從 MySQL 架構(gòu)設(shè)計(jì)出發(fā),看它是如何執(zhí)行一條 SQL 語句的?
●?記一次神奇的 sql 查詢經(jīng)歷,group by 慢查詢優(yōu)化!
●?花了一個(gè)月時(shí)間梳理了一下公司的微服務(wù)核心架構(gòu),原來也不是太難.
●?一次900萬+數(shù)據(jù)量的 SQL 查詢優(yōu)化分析【上百倍性能優(yōu)化】
●?緩存穿透、緩存并發(fā)、緩存失效之思路變遷
●?花了一個(gè)月時(shí)間梳理了一下公司的微服務(wù)核心架構(gòu),原來也不是太難.
●?一文詳解微服務(wù)架構(gòu)
●?API規(guī)范約定
總結(jié)
以上是生活随笔為你收集整理的thread.sleep是让哪个线程休眠_Java多线程:多线程基础知识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zset中的score_Redis 在项
- 下一篇: sqlite查询乘以某列如果是null就