guanxf

          我的博客:http://blog.sina.com.cn/17learning

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            71 隨筆 :: 1 文章 :: 41 評論 :: 0 Trackbacks
          Java多線程技術(shù)  --作者:楊帆    文章連接:http://express.ruanko.com/ruanko-express_6/webpage/tech4.html
           
          項目經(jīng)理:楊帆

          多線程編程一直是學(xué)員們比較頭痛和心虛的地方,因為線程執(zhí)行順序的不可預(yù)知性和調(diào)試時候的困難,讓不少人在面對多線程的情況下選擇了逃避,采用單線程的方式,其實只要我們對線程有了明確的認識,再加上java內(nèi)置的對多線程的天然支持,多線程編程不再是一道難以逾越的鴻溝。

          進程、線程、并發(fā)執(zhí)行

          首先我們先來認識一下進程、線程、并發(fā)執(zhí)行的概念:

            一般來說,當(dāng)運行一個應(yīng)用程序的時候,就啟動了一個進程,當(dāng)然有些會啟動多個進程。啟動進程的時候,操作系統(tǒng)會為進程分配資源,其中最主要的資源是內(nèi)存空間,因為程序是在內(nèi)存中運行的。

          在進程中,有些程序流程塊是可以亂序執(zhí)行的,并且這個代碼塊可以同時被多次執(zhí)行。實際上,這樣的代碼塊就是線程體。線程是進程中亂序執(zhí)行的代碼流程。當(dāng)多個線程同時運行的時候,這樣的執(zhí)行模式成為并發(fā)執(zhí)行。

          下面我以一個日常生活中簡單的例子來說明進程和線程之間的區(qū)別和聯(lián)系:

          雙向多車道道路圖

          這副圖是一個雙向多車道的道路圖,假如我們把整條道路看成是一個“進程”的話,那么圖中由白色虛線分隔開來的各個車道就是進程中的各個“線程”了。

          1. 這些線程(車道)共享了進程(道路)的公共資源(土地資源)。
          2. 這些線程(車道)必須依賴于進程(道路),也就是說,線程不能脫離于進程而存在(就像離開了道路,車道也就沒有意義了)。
          3. 這些線程(車道)之間可以并發(fā)執(zhí)行(各個車道你走你的,我走我的),也可以互相同步(某些車道在交通燈亮?xí)r禁止繼續(xù)前行或轉(zhuǎn)彎,必須等待其它車道的車輛通行完畢)。
          4. 這些線程(車道)之間依靠代碼邏輯(交通燈)來控制運行,一旦代碼邏輯控制有誤(死鎖,多個線程同時競爭唯一資源),那么線程將陷入混亂,無序之中。
          5. 這些線程(車道)之間誰先運行是未知的,只有在線程剛好被分配到CPU時間片(交通燈變化)的那一刻才能知道。

          JVM與多線程

          Java編寫的程序都運行在Java虛擬機(JVM)中,在JVM的內(nèi)部,程序的多任務(wù)是通過線程來實現(xiàn)的。

          每用java命令啟動一個java應(yīng)用程序,就會啟動一個JVM進程。在同一個JVM進程中,有且只有一個進程,就是它自己。在這個JVM環(huán)境中,所有程序代碼的運行都是以線程來運行的。JVM找到程序的入口點main(),然后運行main()方法,這樣就產(chǎn)生了一個線程,這個線程稱之為主線程。當(dāng)main方法結(jié)束后,主線程運行完成。JVM進程也隨即退出。

          操作系統(tǒng)將進程線程進行管理,輪流(沒有固定的順序)分配每個進程很短的一段時間(不一定是均分),然后在每個進程內(nèi)部,程序代碼自己處理該進程內(nèi)部線程的時間分配,多個線程之間相互的切換去執(zhí)行,這個切換時間也是非常短的。

          Java語言對多線程的支持

          Java語言對多線程的支持通過類Thread和接口Runnable來實現(xiàn)。這里就不多說了。這里重點強調(diào)兩個地方:

          // 主線程其它代碼段
          ThreadClass subThread = new ThreadClass();
          subThread.start();
          // 主線程其它代碼段
          subThread.sleep(1000);

          有人認為以下的代碼在調(diào)用start()方法后,肯定是先啟動子線程,然后主線程繼續(xù)執(zhí)行。在調(diào)用sleep()方法后CPU什么都不做,就在那里等待休眠的時間結(jié)束。實際上這種理解是錯誤的。因為:

          1. start()方法的調(diào)用后并不是立即執(zhí)行多線程代碼,而是使得該線程變?yōu)榭蛇\行態(tài)(Runnable),什么時候運行是由操作系統(tǒng)決定的。
          2. Thread.sleep()方法調(diào)用目的是不讓當(dāng)前線程獨自霸占該進程所獲取的CPU資源,以留出一定時間給其他線程執(zhí)行的機會(也就是靠內(nèi)部自己協(xié)調(diào))。

          線程的狀態(tài)切換

          前面我們提到,由于線程何時執(zhí)行是未知的,只有在CPU為線程分配到時間片時,線程才能真正執(zhí)行。在線程執(zhí)行的過程中,由可能會因為各種各樣的原因而暫停(就像前面所舉的例子一樣:汽車只有在交通燈變綠的時候才能夠通行,而且在行駛的過程中可能會出現(xiàn)塞車,等待其它車輛通行或轉(zhuǎn)彎的狀況)。

          這樣線程就有了“狀態(tài)”的概念,下面這副圖很好的反映了線程在不同情況下的狀態(tài)變化。

          線程在不同情況下的狀態(tài)變化

          • 新建狀態(tài)(New):新創(chuàng)建了一個線程對象。
          • 就緒狀態(tài)(Runnable):線程對象創(chuàng)建后,其他線程調(diào)用了該對象的start()方法。該狀態(tài)的線程位于可運行線程池中,變得可運行,等待獲取CPU的使用權(quán)。
          • 運行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
          • 阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因為某種原因放棄CPU使用權(quán),暫時停止運行。直到線程進入就緒狀態(tài),才有機會轉(zhuǎn)到運行狀態(tài)。阻塞的情況分三種:
            1. 等待阻塞:運行的線程執(zhí)行wait()方法,JVM會把該線程放入等待池中。
            2. 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM把該線程放入鎖。
            3. 其他阻塞:運行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請求時,JVM會把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)。
          • 死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。

          Java中線程的調(diào)度API

          Java中關(guān)于線程調(diào)度的API最主要的有下面幾個:

          1. 線程睡眠:Thread.sleep(long millis)方法
          2. 線程等待:Object類中的wait()方法
          3. 線程讓步:Thread.yield() 方法
          4. 線程加入:join()方法
          5. 線程喚醒:Object類中的notify()方法

          關(guān)于這幾個方法的詳細應(yīng)用,可以參考SUN的API。這里我重點總結(jié)一下這幾個方法的區(qū)別和使用。

          sleep方法與wait方法的區(qū)別:

          1. sleep方法是靜態(tài)方法,wait方法是非靜態(tài)方法。
          2. sleep方法在時間到后會自己“醒來”,但wait不能,必須由其它線程通過notify(All)方法讓它“醒來”。
          3. sleep方法通常用在不需要等待資源情況下的阻塞,像等待線程、數(shù)據(jù)庫連接的情況一般用wait。

          sleep/wait與yeld方法的區(qū)別:調(diào)用sleep或wait方法后,線程即進入block狀態(tài),而調(diào)用yeld方法后,線程進入runnable狀態(tài)。

          wait與join方法的區(qū)別:

          1. wait方法體現(xiàn)了線程之間的互斥關(guān)系,而join方法體現(xiàn)了線程之間的同步關(guān)系。
          2. wait方法必須由其它線程來解鎖,而join方法不需要,只要被等待線程執(zhí)行完畢,當(dāng)前線程自動變?yōu)榫途w。
          3. join方法的一個用途就是讓子線程在完成業(yè)務(wù)邏輯執(zhí)行之前,主線程一直等待直到所有子線程執(zhí)行完畢。

          通過上面的介紹相信同學(xué)們對java里面的多線程已經(jīng)有了基本的了解和認識。其實多線程編程并沒有大家想象中的那么難,只要在實際的學(xué)習(xí),工作當(dāng)中不斷的加以練習(xí)和使用,相信大家很快就能掌握其中的奧妙,從而編寫出賞心悅目的java程序。


          posted on 2015-04-11 17:31 管先飛 閱讀(270) 評論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 威海市| 永兴县| 宜都市| 桃园县| 康马县| 修水县| 锦屏县| 隆子县| 阜城县| 奉节县| 平泉县| 苏尼特右旗| 凤山市| 无极县| 绥芬河市| 佳木斯市| 富裕县| 留坝县| 哈巴河县| 游戏| 皋兰县| 阿瓦提县| 石泉县| 福泉市| 海安县| 仁布县| 赤壁市| 大石桥市| 玉屏| 汉源县| 广德县| 黔东| 禹城市| 大邑县| 望都县| 靖宇县| 上蔡县| 绥江县| 英吉沙县| 甘泉县| 皮山县|