
? ? ? ? ? ? ? ? ? ? 過(guò)硬的技術(shù)?+?透徹的金融業(yè)務(wù) + 心理學(xué) =?互聯(lián)網(wǎng)金融專(zhuān)家
寫(xiě)在文章前面的:在之前的Java中級(jí)甚至Java的高級(jí)開(kāi)發(fā)面試中,一般java的多線程開(kāi)發(fā)就是問(wèn)問(wèn)基本問(wèn)題,但今年隨著社會(huì)大經(jīng)濟(jì)環(huán)境的變化,和過(guò)剩的開(kāi)發(fā)人員的涌現(xiàn),所以市場(chǎng)要求也越來(lái)越高,多線程也成了必須要重點(diǎn)掌握的知識(shí)點(diǎn)。
多線程和多任務(wù)往往是使用多處理器系統(tǒng)的最合理方式。因?yàn)椴l(fā)在單處理器上執(zhí)行是沒(méi)有意義的,在單處理器上并發(fā)和順序的執(zhí)行并沒(méi)有什么本質(zhì)的區(qū)別。
使用多線程的好處就是更好的利用CPU的性能,更好的用戶體驗(yàn)等。
一、線程的基本知識(shí)
了解線程之前必須要了解進(jìn)程了,進(jìn)程是指運(yùn)行中的應(yīng)用程序,每個(gè)進(jìn)程都有自己獨(dú)立的地址空間(內(nèi)存空間),比如用戶點(diǎn)擊桌面的IE瀏覽器,就啟動(dòng)了一個(gè)進(jìn)程,操作系統(tǒng)就會(huì)為該進(jìn)程分配獨(dú)立的地址空間。當(dāng)用戶再次點(diǎn)擊左面的IE瀏覽器,又啟動(dòng)了一個(gè)進(jìn)程,操作系統(tǒng)將為新的進(jìn)程分配新的獨(dú)立的地址空間。目前操作系統(tǒng)都支持多進(jìn)程。
而線程是進(jìn)程的一個(gè)實(shí)體,是被系統(tǒng)獨(dú)立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其他線程共享進(jìn)程所擁有的全部資源。一個(gè)線程可以創(chuàng)建和撤銷(xiāo)另一個(gè)線程,同一個(gè)進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。
二、線程的狀態(tài)
線程一共有五種狀態(tài)(等待和超時(shí)等待歸屬于等待):New(新建),Runnable(可運(yùn)行),Blocked(阻塞狀態(tài)),Waiting(等待狀態(tài) 和? Timed Waiting(超時(shí)等待)) ,Terminated(終止)

三、常用方法詳解
常用方法有:start(),run(),wait(),sleep(),notify(),notifyAll(),join(),yield(),park(),unpark(),join(),synchronized 方法或方法塊。
1、start() :?jiǎn)?dòng)相應(yīng)線程,該方法的返回并不代表相應(yīng)的線程被啟動(dòng)。一個(gè)Thread實(shí)例的start方法只能夠被調(diào)用一次,多次調(diào)用會(huì)導(dǎo)致異常的拋出。
2、run():用于實(shí)現(xiàn)線程的任務(wù)處理邏輯。該方法是由Java虛擬機(jī)直接調(diào)用的,一般情況下應(yīng)用程序不應(yīng)該調(diào)用該方法。
3、wait():讓線程等待或者使用wait(3000)的方式將線程阻塞3秒鐘,sleep()方法也是可以將線程休眠或者休眠一定時(shí)間的,兩個(gè)方法都可以引起阻塞。
4、notify() 是喚醒線程,API文檔:Wakes up a single thread that is waiting on this object's monitor。 NotifyAll() 在API文檔中解釋為:Wakes up all threads that are waiting on this object's monitor.(歡迎所有線程,可以作用于wait(),當(dāng)然也可以作用于sleep()的線程)。
notify():?jiǎn)拘岩粋€(gè)處于等待狀態(tài)的線程,在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且與優(yōu)先級(jí)無(wú)關(guān);
notityAll():?jiǎn)拘阉刑幱诘却隣顟B(tài)的線程,該方法并不是將對(duì)象的鎖給所有線程,而是讓它們競(jìng)爭(zhēng),只有獲得鎖的線程才能進(jìn)入就緒狀態(tài);
5、park() 和unpark(),unpark函數(shù)為線程提供“許可(permit)”,線程調(diào)用park函數(shù)則等待“許可”。這個(gè)有點(diǎn)像信號(hào)量,但是這個(gè)“許可”是不能疊加的,“許可”是一次性的。比如線程B連續(xù)調(diào)用了三次unpark函數(shù),當(dāng)線程A調(diào)用park函數(shù)就使用掉這個(gè)“許可”,如果線程A再次調(diào)用park,則進(jìn)入等待狀態(tài)。注意,unpark函數(shù)可以先于park調(diào)用。比如線程B調(diào)用unpark函數(shù),給線程A發(fā)了一個(gè)“許可”,那么當(dāng)線程A調(diào)用park時(shí),它發(fā)現(xiàn)已經(jīng)有“許可”了,那么它會(huì)馬上再繼續(xù)運(yùn)行。
6、join(),該方法只會(huì)使主線程進(jìn)入等待池并等待t線程執(zhí)行完畢后才會(huì)被喚醒。并不影響同一時(shí)刻處在運(yùn)行狀態(tài)的其他線程。
7、yield(),調(diào)用yield方法會(huì)讓當(dāng)前線程交出CPU權(quán)限,讓CPU去執(zhí)行其他的線程。它跟sleep方法類(lèi)似,同樣不會(huì)釋放鎖。但是yield不能控制具體的交出CPU的時(shí)間,另外,yield方法只能讓擁有相同優(yōu)先級(jí)的線程有獲取CPU執(zhí)行時(shí)間的機(jī)會(huì)。而且,調(diào)用yield方法并不會(huì)讓線程進(jìn)入阻塞狀態(tài),而是讓線程重回就緒狀態(tài),它只需要等待重新獲取CPU執(zhí)行時(shí)間,這一點(diǎn)是和sleep方法不一樣的。
wait()和sleep()的區(qū)別是:
? ? ?a:wait方法是作用在Object上的,而sleep是作用在線程上的,當(dāng)調(diào)用Thread.sleep時(shí),并不能改變對(duì)象的狀態(tài)。
證據(jù)如下:

? b:最主要是sleep方法沒(méi)有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法(鎖代碼塊和方法鎖)。
? c:wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用(使用范圍)?
? d:sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常?
sleep相當(dāng)于讓線程睡眠,交出CPU,讓CPU去執(zhí)行其他的任務(wù)。
注意:sleep方法不會(huì)釋放鎖,也就是說(shuō)如果當(dāng)前線程持有對(duì)某個(gè)對(duì)象的鎖,則即使調(diào)用sleep方法,其他線程也無(wú)法訪問(wèn)這個(gè)對(duì)象。如果調(diào)用了sleep方法,必須捕獲InterruptedException異常或者將該異常向上層拋出。當(dāng)線程睡眠時(shí)間滿后,不一定會(huì)立即得到執(zhí)行,因?yàn)榇藭r(shí)可能CPU正在執(zhí)行其他的任務(wù)。所以說(shuō)調(diào)用sleep方法相當(dāng)于讓線程進(jìn)入阻塞狀態(tài)。