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