JavaSE学习52:细说多线程之Thread类和Runable接口
一線程創(chuàng)建的兩種方式比較
? ? ? 線程創(chuàng)建和啟動有兩種方式,這里只是列出步驟,不再進(jìn)行詳細(xì)解釋。
? ? ??(1)繼承Thread類
[java]?view plaincopy print?
? ? ??(2)實(shí)現(xiàn)Runnable接口
[java]?view plaincopy print?
? ? ??(3)兩種方式的比較
? ? ? 1)Runnable方式可以避免Thread方式由于Java單繼承特性帶來的缺陷。
? ? ? 2)Runnable方式的代碼可以被多個線程(Thread實(shí)例)共享,適合于多個線程處理同一資源的情況。
? ? ??二模擬應(yīng)用場景
? ? ? 模擬一個火車站買票的場景,某車次還剩下5張火車票,有三個窗口去賣這5張火車票,我們使用三個線程模擬三
個窗口同時賣這5張火車票,我們看Thread方式和Runnable方式這兩種方式模擬出一個什么樣的結(jié)果。
? ? ??(1)使用Thread方式模擬買票
? ? ? TicketsThread.java源文件代碼:
[java]?view plaincopy print?
? ? ? 運(yùn)行結(jié)果:
? ? ? 得到的結(jié)果并不是我們想要的結(jié)果。
? ? ? 在票的數(shù)量加static修飾關(guān)鍵字得到的結(jié)果是正確的。這里不再進(jìn)行演示。
? ? ??(2)使用Runnable方式模擬買票
? ? ? TicketsRunnable.java源文件代碼:
[java]?view plaincopy print?
? ? ? 得到了預(yù)期的結(jié)果:
? ? ? ?(3)結(jié)果分析
? ? ? ?由于線程的執(zhí)行是隨機(jī)的,打印的結(jié)果也是隨機(jī)的。
? ? ? ?Thread方式
? ? ? ?三個線程,創(chuàng)建了三個Thread對象,每個線程都有自己的Thread對象,都有自己的ticketsCount變量,它們?nèi)齻€
線程并不是共享ticketsCount變量,也就是每個線程都可以賣出5張火車票,即三個窗口賣出去15張火車票。
? ? ? ?Runnable方式
? ? ? ?三個線程共用一個Runnable對象,也就是三個線程共用一個ticketsCount變量,即三個窗口一共賣了5張火車票。
? ? ? ?前者不是一個線程三個對象,是三個Thread對象,也是三個線程,這三個線程啟動后都會執(zhí)行5次賣票,實(shí)現(xiàn)不
了共享“5張票”這個資源,所以輸出就會有15張票賣出去,顯然不符合實(shí)際,用Runnable就可以解決這個問題,創(chuàng)建
的三個線程可以共享"5張票"這個資源。
? ? ? ticketsCont變量是實(shí)例變量,它的值自然是存在堆中(每個java對象在堆中都會占據(jù)一定內(nèi)存,而實(shí)例變量的值就
是存儲在這塊內(nèi)存中,類似于結(jié)構(gòu)體,因此每個對象對應(yīng)一個ticketsCont的值),ticketsCont跟值傳遞沒有關(guān)系啊,如
果是Runnable方式的話,傳遞的也只是MyThread對象引用的副本,不管ticketsCont的事,但是因?yàn)閠icketsCont的值
在引用和引用副本所指向的堆內(nèi)存中,所以無論是引用還是引用副本改變了堆內(nèi)存中ticketsCont的值,都會產(chǎn)生效
果!
? ? ? ?這個根據(jù)你的需要來操作,這樣說吧,如果有一個比較大的資源要你下載,那么你用Thread方式那么你就只能一
個線程去吧這個資源下載完,如果是runable方式的話你就可以?多new幾個子線程來出來,通過共享runable對象里面
的資源來用多個子線程來下載這個資源,這樣的話,下載資源的時候 runable方法會使下載的線程多一些幾率在cpu里
面,也會讓你下載速度變快繼承Thread類是多個線程分別完成自己的任務(wù),實(shí)現(xiàn)Runnable接口是多個線程共同完成
一個任務(wù)。
? ? ? ?三線程的生命周期
? ? ? ?線程的生命周期轉(zhuǎn)換示意圖:
? ? ? ?線程的生命周期:
? ? ? ?1)創(chuàng)建:新建一個線程對象,如Thread thd=new Thread()。
? ? ? ?2)就緒:創(chuàng)建了線程對象后,調(diào)用了線程的start()方法(注意:此時線程只是進(jìn)入了線程隊(duì)列,等待獲取CPU服
務(wù),具備了運(yùn)行的條件,但并不一定已經(jīng)開始運(yùn)行了)。
? ? ? ?3)運(yùn)行:處于就緒狀態(tài)的線程,一旦獲取了CPU資源,便進(jìn)入到運(yùn)行狀態(tài),開始執(zhí)行run()方法里面的邏輯。
? ? ? ?4)阻塞:一個正在執(zhí)行的線程在某些情況下,由于某種原因而暫時讓出了CPU資源,暫停了自己的執(zhí)行,便進(jìn)入
了阻塞狀態(tài),如調(diào)用了sleep()方法。
? ? ? 5)終止:線程的run()方法執(zhí)行完畢,或者線程調(diào)用了stop()方法,線程便進(jìn)入終止?fàn)顟B(tài)。
? ? ? 這里我們可以用一個經(jīng)典的線問題就是生產(chǎn)者和消費(fèi)者問題的實(shí)例:
[java]?view plaincopy print?
? ? ? 運(yùn)行結(jié)果:
? ? ??關(guān)于一些問題的解析:
? ? ? 執(zhí)行線程sleep()方法是依然占著cpu的,操作系統(tǒng)認(rèn)為該當(dāng)前線程正在運(yùn)行,不會讓出系統(tǒng)資源。
? ? ? 執(zhí)行wait()方法是讓線程到等待池等待,讓出一系列的系統(tǒng)資源,其他線程可以根據(jù)調(diào)度占用cpu線程的資源有不
少,但應(yīng)該包含CPU資源和鎖資源這兩類。
? ? ? sleep(long mills):讓出CPU資源,但是不會釋放鎖資源。
? ? ? wait():讓出CPU資源和鎖資源。
? ? ? 鎖是用來線程同步的,sleep(long mills)雖然讓出了CPU,但是不會讓出鎖,其他線程可以利用CPU時間片了,但
如果其他線程要獲取sleep(long mills)擁有的鎖才能執(zhí)行,則會因?yàn)闊o法獲取鎖而不能執(zhí)行,繼續(xù)等待。但是那些沒有
和sleep(long mills)競爭鎖的線程,一旦得到CPU時間片即可運(yùn)行了。
? ? ? ?四守護(hù)線程
? ? ? ?(1)守護(hù)線程理論知識
? ? ? ?Java線程分為兩類:
? ? ? ?1)用戶線程:運(yùn)行在前臺,執(zhí)行具體的任務(wù)。程序的主線程、連接網(wǎng)絡(luò)的子線程等都是用戶線程。
? ? ? ?2)守護(hù)線程:運(yùn)行在后臺,為其他前臺線程服務(wù)。守護(hù)線程的特點(diǎn)是一旦所有用戶線程都結(jié)束運(yùn)行,守護(hù)線程會
隨JVM一起結(jié)束工作。守護(hù)線程的應(yīng)用:數(shù)據(jù)庫連接池中的檢測線程和JVM虛擬機(jī)啟動后的檢測線程等等。最常見的
守護(hù)線程:垃圾回收線程
? ? ? ?設(shè)置守護(hù)線程:
? ? ? ?可以通過調(diào)用Thread類的setDaemon(true)方法來設(shè)置當(dāng)前的線程為守護(hù)線程。
? ? ? ?使用守護(hù)線程的注意事項(xiàng):
? ? ? ?1)setDaemon(true)必須在start()方法之前調(diào)用,否則會拋出IllegalThreadStateException異常。
? ? ? ?2)在守護(hù)線程中產(chǎn)生的新線程也是守護(hù)線程。
? ? ? ?3)不是所有的任務(wù)都可以分配給守護(hù)線程來執(zhí)行,比如讀寫操作或者計(jì)算邏輯。
? ? ? ?(2)守護(hù)線程代碼示例:
[java]?view plaincopy print?
? ? ? ?運(yùn)行結(jié)果:
? ? ? ?(3)使用jstack生成線程快照
? ? ? ?作用:生成JVM當(dāng)前時刻線程的快照(threaddump,即當(dāng)前進(jìn)程中所有線程的信息)。
? ? ? ?目的:幫助定位程序問題出現(xiàn)的原因,如長時間停頓、CPU占用率高等。
? ? ? ? jstack命令行工具
? ? ? ?你安裝JDK安裝目錄下的bin文件夾下:
? ? ? ?位置:E:\Java\develop\jdk1.8.0_25\bin
? ? ? ?如何使用jstack
? ? ? ?在cmd中輸入:jstack
? ? ? ?我們?nèi)ト蝿?wù)管理器中找到一個進(jìn)程的PID:
? ? ? ?生成快照:
? ? ? ?五總結(jié)
? ? ? ?(1)怎樣解決死鎖的問題
? ? ? ?1)盡量避免不必要的synchronized關(guān)鍵字。
? ? ? ?2)可以用其他方法替換synchronized關(guān)鍵字,比如標(biāo)志不可變量。
? ? ? ?3)保證synchronized代碼塊簡練。
? ? ? ?(2)創(chuàng)建線程的建議
? ? ? ?根據(jù)兩種創(chuàng)建線程方法的比較,得出的結(jié)論是使用實(shí)現(xiàn)Runnable接口的方法創(chuàng)建的多線程更好一些,另外,就是
需要重點(diǎn)注意,程序中的同一個資源指的是同一個Runnable對象。建議多使用Runnable這種方式創(chuàng)建多線程。
? ? ? ?(3)補(bǔ)充:
? ? ? 1)程序中的同一資源指的是同一個Runnable對象。
? ? ? 2)安全的賣票程序中需要加入同步(Synchronized)。我們的代碼過程中并沒有加入,如果需要完善,就必須加入
同步鎖。
from:?http://blog.csdn.net/erlian1992/article/details/51707369
總結(jié)
以上是生活随笔為你收集整理的JavaSE学习52:细说多线程之Thread类和Runable接口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaSE入门学习51:多线程编程(二
- 下一篇: JavaSE学习53:细说多线程之内存可