接觸到java,若是要繼續深刻學習,那麼就必需要了解線程,這個過程是比較長的。若是在實際工做中沒有接觸到,本身摸索的話,始終不能得其道,固然這並不妨礙,咱們將線程基礎學習一翻。之後有了實際的需求驅動,那麼就會事半功倍。java
在學習的時候,線程的狀態轉換,線程調度,同步異步,線程的通訊這些基本的知識固然要緊緊掌握。在摸索中前進。編程
在這裏爲你們推薦一個網站 併發編程網多線程
圖示說明:併發
一、新建狀態:新建了一個線程對象。異步
二、就緒狀態(Runnable):線程對象建立後,其餘線程對象調用了該線程對象的start()方法,該狀態的線程位於可運行的線程池中,變得能夠運行,等待得到cpu使用權。jvm
三、運行狀態(running):就緒狀態的線程得到cpu,開始執行該線程的代碼。學習
四、阻塞狀態(Blocked):阻塞狀態是由於,線程因爲某種緣由放棄cpu使用權,暫時中止運行。知道線程進入就緒狀態,纔有機會轉到運行狀態。阻塞狀態狀況分爲三種:(該狀態爲中止當前線程,當時並不釋放其所佔用的資源)網站
4.1等待阻塞:運行的線程執行了wait,jvm會把該線程放入等待的池中。spa
4.2同步阻塞:運行的線程在獲取對象同步鎖時,若該同步鎖被別人的線程佔用,則該jvm會把線程放入鎖定池中。.net
4.3其餘阻塞:運行的線程執行執行sleep()或者join()方法,或者發出io請求時,jvm會將該線程設置爲阻塞。當sleep()狀態超時,join等待線程終止或超時,或者io處理完畢,線程從新轉爲就緒狀態。
五、死亡狀態(dead):線程執行完了,或者因異常退出了run方法,該線程生命週期結束。
(一)當線程調用了自身的sleep()或者其餘線程的join()方法時,就會進入阻塞狀態,當sleep()或者join方法結束後,該線程轉爲可運行狀態,繼續等待os分配cpu時間片
(二)線程調用了yield方法,意思是放棄當前的cpu時間分片,回到可運行狀態,這時與其餘可運行狀態,這時與其餘進程處於同等競爭狀態,os有可能會接着讓這個線程進入運行狀態;(注:yield並非讓線程進入就緒狀態,而是運行狀態,可是不佔有cpu時間片而已)
(三)當線程剛進入可運行狀態(即就緒狀態),發現將要調用的資源被synchroniza(同步),獲取不到鎖標記,將會當即進入鎖池狀態,等待獲取鎖標記(這時的鎖定池裏也許已經有了其餘線程在等待獲取鎖標記,這時他們處於隊列狀態,即先到先得),一旦線程得到線程鎖標記後,就可轉入可運行狀態,等待os分配cpu時間片;
(四)當線程調用wait()方法後進入等待隊列(進入到這個狀態會釋放所佔有的資源,與阻塞不一樣),這個狀態是不可以被自動喚醒的,必須依賴於其餘線程調用notify()或者notifyall()方法才喚醒(wait(1000)能夠自動喚醒)(因爲notify()只是喚醒一個線程,但咱們不能肯定具體喚醒的是那個線程,也許咱們須要喚醒的線程不可以被喚醒,所以在實際使用時,通常都使用notifyall()方法,喚醒全部的線程),線程被喚醒後會進入鎖池,等待獲取鎖標記
(五)wait()和notify()方法:當一個線程執行到wait()方法時,他就進入到一個和對象相關的等待池中,同時失去了對象鎖。當他被一個notify()方法喚醒的時,等待池中的線程就被放到了鎖定池中。該線程從池中獲的鎖,而後回到wait()方法前的中斷現場。
調用sleep,join時,不會釋放所佔用的資源,因此會進入到阻塞狀態。
調用wait時,會釋放所佔用的資源,因此會進入到等待隊列。
更加細緻的圖示:
注:幾個線程方法的介紹:
一、wait()
public final void wait()throws InterruptedException,IllegalMonitorStateException
該方法用來將當前線程置入休眠狀態,直到接到通知或被中斷爲止。在調用wait()以前,線程必需要得到該對象的對象級別鎖,即只能在同步方法或同步塊中調用wait()方法。進入wait()方法後,當前線程釋放鎖。在從wait()返回前,線程與其餘線程競爭從新得到鎖。若是調用wait()時,沒有持有適當的鎖,則拋出IllegalMonitorStateException,它是RuntimeException的一個子類,所以,不須要try-catch結構。
二、notify()
public final native void notify() throws IllegalMonitorStateException
該方法也要在同步方法或同步塊中調用,即在調用前,線程也必需要得到該對象的對象級別鎖,的若是調用notify()時沒有持有適當的鎖,也會拋出IllegalMonitorStateException。
該方法用來通知那些可能等待該對象的對象鎖的其餘線程。若是有多個線程等待,則線程規劃器任意挑選出其中一個wait()狀態的線程來發出通知,並使它等待獲取該對象的對象鎖(notify後,當前線程不會立刻釋放該對象鎖,wait所在的線程並不能立刻獲取該對象鎖,要等到程序退出synchronized代碼塊後,當前線程纔會釋放鎖,wait所在的線程也才能夠獲取該對象鎖),但不驚動其餘一樣在等待被該對象notify的線程們。當第一個得到了該對象鎖的wait線程運行完畢之後,它會釋放掉該對象鎖,此時若是該對象沒有再次使用notify語句,則即使該對象已經空閒,其餘wait狀態等待的線程因爲沒有獲得該對象的通知,會繼續阻塞在wait狀態,直到這個對象發出一個notify或notifyAll。這裏須要注意:它們等待的是被notify或notifyAll,而不是鎖。這與下面的notifyAll()方法執行後的狀況不一樣。
三、notifyAll()
public final native void notifyAll() throws IllegalMonitorStateException
該方法與notify()方法的工做方式相同,重要的一點差別是:
notifyAll使全部原來在該對象上wait的線程通通退出wait的狀態(即所有被喚醒,再也不等待notify或notifyAll,但因爲此時尚未獲取到該對象鎖,所以還不能繼續往下執行),變成等待獲取該對象上的鎖,一旦該對象鎖被釋放(notifyAll線程退出調用了notifyAll的synchronized代碼塊的時候),他們就會去競爭。若是其中一個線程得到了該對象鎖,它就會繼續往下執行,在它退出synchronized代碼塊,釋放鎖後,其餘的已經被喚醒的線程將會繼續競爭獲取該鎖,一直進行下去,直到全部被喚醒的線程都執行完畢。
四、wait(long)和wait(long,int)
顯然,這兩個方法是設置等待超時時間的,後者在超值時間上加上ns,精度也難以達到,所以,該方法不多使用。對於前者,若是在等待線程接到通知或被中斷以前,已經超過了指定的毫秒數,則它經過競爭從新得到鎖,並從wait(long)返回。另外,須要知道,若是設置了超時時間,當wait()返回時,咱們不能肯定它是由於接到了通知仍是由於超時而返回的,由於wait()方法不會返回任何相關的信息。但通常能夠經過設置標誌位來判斷,在notify以前改變標誌位的值,在wait()方法後讀取該標誌位的值來判斷,固然爲了保證notify不被遺漏,咱們還須要另一個標誌位來循環判斷是否調用wait()方法。
深刻理解:
若是線程調用了對象的wait()方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
當有線程調用了對象的notifyAll()方法(喚醒全部wait線程)或notify()方法(只隨機喚醒一個wait線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。
優先級高的線程競爭到對象鎖的機率大,倘若某線程沒有競爭到該對象鎖,它還會留在鎖池中,惟有線程再次調用wait()方法,它纔會從新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了synchronized代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。
幾個線程方法介紹轉載於「http://blog.csdn.net/ns_code/article/details/17225469」 已經寫得夠清楚了,不必我再贅述一次,再根據我文章裏面畫的線程狀態轉換圖,會很清晰的理解線程狀態轉換。
注:如下圖是我照着網友的圖畫的。
注:借鑑網上資料進行整理,再加上本身的理解,將圖從新畫了一遍。目的是可以更加清晰的理解線程狀態轉換,線程狀態轉換是寫好多線程的代碼的基礎。
再貼其餘相似的文章,http://my.oschina.net/zhdkn/blog/114301,http://my.oschina.net/mingdongcheng/blog/139263