qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          Java中的線程Thread總結

           首先來看一張圖,下面這張圖很清晰的說明了線程的狀態與Thread中的各個方法之間的關系,很經典的!
            在Java中創建線程有兩種方法:使用Thread類和使用Runnable接口。
            要注意的是Thread類也實現了Runnable接口,因此,從Thread類繼承的類的實例也可以作為target傳入這個構造方法??赏ㄟ^這種方法實現多個線程的資源共享。
            線程的生命周期:
            1.新建狀態(New):用new語句創建的線程對象處于新建狀態,此時它和其它的java對象一樣,僅僅在堆中被分配了內存
            2.就緒狀態(Runnable):當一個線程創建了以后,其他的線程調用了它的start()方法,該線程就進入了就緒狀態。處于這個狀態的 線程位于可運行池中,等待獲得CPU的使用權
            3.運行狀態(Running): 處于這個狀態的線程占用CPU,執行程序的代碼
            4.阻塞狀態(Blocked): 當線程處于阻塞狀態時,java虛擬機不會給線程分配CPU,直到線程重新進入就緒狀態,它才有機會轉到 運行狀態。
            阻塞狀態分為三種情況:
            1)、 位于對象等待池中的阻塞狀態:當線程運行時,如果執行了某個對象的wait()方法,java虛擬機就回把線程放到這個對象的等待池中
            2)、 位于對象鎖中的阻塞狀態,當線程處于運行狀態時,試圖獲得某個對象的同步鎖時,如果該對象的同步鎖已經被其他的線程占用,JVM就會把這個線程放到這個對象的瑣池中。
            3)、 其它的阻塞狀態:當前線程執行了sleep()方法,或者調用了其它線程的join()方法,或者發出了I/O請求時,就會進入這個狀態中。
            一、創建并運行線程
            當調用start方法后,線程開始執行run方法中的代碼。線程進入運行狀態??梢酝ㄟ^Thread類的isAlive方法來判斷線程是否處于運行狀態。當線程處于運行狀態時,isAlive返回true,當isAlive返回false時,可能線程處于等待狀態,也可能處于停止狀態。
            二、掛起和喚醒線程
            一但線程開始執行run方法,就會一直到這個run方法執行完成這個線程才退出。但在線程執行的過程中,可以通過兩個方法使線程暫時停止執行。這兩個方法是suspend和sleep。在使用suspend掛起線程后,可以通過resume方法喚醒線程。而使用sleep使線程休眠后,只能在設定的時間后使線程處于就緒狀態(在線程休眠結束后,線程不一定會馬上執行,只是進入了就緒狀態,等待著系統進行調度)。
            雖然suspend和resume可以很方便地使線程掛起和喚醒,但由于使用這兩個方法可能會造成一些不可預料的事情發生,因此,這兩個方法被標識為deprecated(棄用)標記,這表明在以后的jdk版本中這兩個方法可能被刪除,所以盡量不要使用這兩個方法來操作線程。
            三、終止線程的三種方法
            有三種方法可以使終止線程。
            1.  使用退出標志,使線程正常退出,也就是當run方法完成后線程終止。
            2.  使用stop方法強行終止線程(線程中調用了阻塞代碼)(這個方法不推薦使用,因為stop是依靠拋出異常來結束線程的,也可能發生不可預料的結果)。如果沒有調用阻塞代碼,可以正常結束線程。
            3.  使用interrupt方法中斷線程(線程中調用了阻塞代碼)(其實這種方法也是通過拋出異常來結束線程的)。如果沒有調用阻塞代碼,可以通過判斷線程的中斷標志位來介紹線程。
            線程的幾個方法:
            join():等待此線程死亡后再繼續,可使異步線程變為同步線程
            interrupt():中斷線程,被中斷線程會拋InterruptedException
            wait():等待獲取鎖:表示等待獲取某個鎖執行了該方法的線程釋放對象的鎖,JVM會把該線程放到對象的等待池中。該線程等待其它線程喚醒
            notify():執行該方法的線程喚醒在對象的等待池中等待的一個線程,JVM從對象的等待池中隨機選擇一個線程,把它轉到對象的鎖池中。使線程由阻塞隊列進入就緒狀態
            sleep():讓當前正在執行的線程休眠,有一個用法可以代替yield函數——sleep(0)
            yield():暫停當前正在執行的線程對象,并執行其他線程。也就是交出CPU一段時間(其他同樣的優先級或者更高優先級的線程可以獲取到運行的機會)  sleep和yield區別:
            1、sleep()方法會給其他線程運行的機會,而不考慮其他線程的優先級,因此會給較低線程一個運行的機會;yield()方法只會給相同優先級或者更高優先級的線程一個運行的機會。
            2、當線程執行了sleep(long millis)方法后,將轉到阻塞狀態,參數millis指定睡眠時間;當線程執行了yield()方法后,將轉到就緒狀態。
            3、sleep()方法聲明拋出InterruptedException異常,而yield()方法沒有聲明拋出任何異常
            4、sleep()方法比yield()方法具有更好的移植性
            如果希望明確地讓一個線程給另外一個線程運行的機會,可以采取以下的辦法之一:
            1、調整各個線程的優先級
            2、讓處于運行狀態的線程調用Thread.sleep()方法
            3、讓處于運行狀態的線程調用Thread.yield()方法
            4、讓處于運行狀態的線程調用另一個線程的join()方法
            首先,wait()和notify(),notifyAll()是Object類的方法,sleep()和yield()是Thread類的方法。
            (1).常用的wait方法有wait()和wait(long timeout):
            void wait() 在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待。
            void wait(long timeout) 在其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量前,導致當前線程等待。
            wait()后,線程會釋放掉它所占有的“鎖標志”,從而使線程所在對象中的其它synchronized數據可被別的線程使用。
            wait()和notify()因為會對對象的“鎖標志”進行操作,所以它們必須在synchronized函數或synchronized代碼塊中進行調用。如果在non-    synchronized函數或non-synchronized代碼塊中進行調用,雖然能編譯通過,但在運 行時會發生IllegalMonitorStateException的異常。
            (2).Thread.sleep(long millis),必須帶有一個時間參數。
            sleep(long)使當前線程進入停滯狀態,所以執行sleep()的線程在指定的時間內肯定不會被執行;
            sleep(long)可使優先級低的線程得到執行的機會,當然也可以讓同優先級和高優先級的線程有執行的機會;
            sleep(long)是不會釋放鎖標志的。
            (3).yield()沒有參數。
            sleep 方法使當前運行中的線程睡眼一段時間,進入不可運行狀態,這段時間的長短是由程序設定的,yield 方法使當前線程讓出CPU占有權,但讓出的時間是不可設定的。yield()也不會釋放鎖標志。
            實際上,yield()方法對應了如下操作: 先檢測當前是否有相同優先級的線程處于同可運行狀態,如有,則把 CPU 的占有權交給此線程,否則繼續運行原來的線程。所以yield()方法稱為“退讓”,它把運行機會讓給了同等優先級的其他線程。
            sleep方法允許較低優先級的線程獲得運行機會,但yield()方法執行時,當前線程仍處在可運行狀態,所以不可能讓出較低優先級的線程些時獲得CPU占有權。 在一個運行系統中,如果較高優先級的線程沒有調用 sleep 方法,又沒有受到 I/O阻塞,那么較低優先級線程只能等待所有較高優先級的線程運行結束,才有機會運行。
            yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行。所以yield()只能使同優先級的線程有執行的機會。
            volitile 語義
            volatile相當于synchronized的弱實現,也就是說volatile實現了類似synchronized的語義,卻又沒有鎖機制。它確保對volatile字段的更新以可預見的方式告知其他的線程。
            volatile包含以下語義:
            (1)Java 存儲模型不會對valatile指令的操作進行重排序:這個保證對volatile變量的操作時按照指令的出現順序執行的。
           ?。?)volatile變量不會被緩存在寄存器中(只有擁有線程可見)或者其他對CPU不可見的地方,每次總是從主存中讀取volatile變量的結果。也就是說對于volatile變量的修改,其它線程總是可見的,并且不是使用自己線程棧內部的變量。也就是在happens-before法則中,對一個valatile變量的寫操作后,其后的任何讀操作理解可見此寫操作的結果。
            盡管volatile變量的特性不錯,但是volatile并不能保證線程安全的,也就是說volatile字段的操作不是原子性的,volatile變量只能保證可見性(一個線程修改后其它線程能夠理解看到此變化后的結果),要想保證原子性,目前為止只能加鎖!
            數據同步:
            線程同步的特征:
            1、如果一個同步代碼塊和非同步代碼塊同時操作共享資源,仍然會造成對共享資源的競爭。因為當一個線程執行一個對象的同步代碼塊時,其他的線程仍然可以執行對象的非同步代碼塊。(所謂的線程之間保持同步,是指不同的線程在執行同一個對象的同步代碼塊時,因為要獲得對象的同步鎖而互相牽制)
            2、每個對象都有唯一的同步鎖
            3、在靜態方法前面可以使用synchronized修飾符,但是要注意的是鎖對象是類(用Object.class而不能用this),而不是這個類的對象。
            4、當一個線程開始執行同步代碼塊時,并不意味著必須以不間斷的方式運行,進入同步代碼塊的線程可以執行Thread.sleep()或者執行Thread.yield()方法,此時它并不釋放對象鎖,只是把運行的機會讓給其他的線程。
            5、synchronized聲明不會被繼承,如果一個用synchronized修飾的方法被子類覆蓋,那么子類中這個方法不在保持同步,除非用synchronized修飾。
            6、synchronized 關鍵字能夠修飾一個對象實例中的函數或者代碼塊。 在一個非靜態方法中 this 關鍵字表示當前的實例對象。 在一個 synchronized 修飾的靜態的方法中,這個方法所在的類使用 Class 作為實例對象

          posted on 2014-03-11 11:09 順其自然EVO 閱讀(280) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2014年3月>
          2324252627281
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 高邑县| 陕西省| 乐至县| 沾益县| 榆中县| 宣汉县| 通海县| 嘉定区| 玉溪市| 临夏市| 故城县| 册亨县| 贡嘎县| 拜城县| 内丘县| 海林市| 婺源县| 安阳市| 安西县| 大同县| 朝阳市| 泰安市| 望江县| 阳信县| 兴安县| 邹城市| 岳池县| 淮北市| 佛坪县| 新余市| 南皮县| 攀枝花市| 磐石市| 凤山县| 多伦县| 无棣县| 桃源县| 江口县| 嘉定区| 罗山县| 吴川市|