天天看點

黑馬程式員——Java多線程

多線程基礎知識

程序是一個正在執行的程式。

cpu在同時執行這些程式,其實是跳躍式的,做快速的切換,時間很短。一個程序可能存在多條路徑。迅雷的多路徑。每一個進行執行都有一個執行順序,該順序是一個執行路徑,或這叫一個控制單元。每一個程序至少有一個線程,線程就是程序中的一個獨立的控制單元,線程控制程序的執行。。jvm啟動的時候會有一個程序就叫做java.exe,該程序中至少有一個線程在控制Java程式的執行

,而且該線程的執行代碼在

主函數中。該線程稱為住線程。虛拟機至少也有兩個線程,一個主線程執行,另一個負責垃圾回收的線程。下載下傳就是多線程的。

存在的意義,能産生同時運作多段代碼。

繼承thread類

建立線程的第一種方式:繼承thread類

線程已經被Java封裝起來了,其實是windows幫你建立。

thread:允許程式并發地執行多個線程。成為thread的子類,并且重寫run(runable對象。)

繼承thread:

1.建立好一個對象就建立好一個線程

1.定義類執行thread

2.複寫run方法。

3.調用線程的start方法,這個方法。

public class Rocket extends Thread {

private int count;

private static int num= 1;

private final int id= num++;

public Rocket( int count){ this. count= count; }

@Override

public void run (){

           while (count >0 )

          System . out. println( "第" +id +"号火箭發射倒計時:" +count --) ;

}

           public static void main ( String[] args ) {

                    // TODO Auto-generated method

stub

Thread t =new Rocket (10 );

Thread t2 =new Rocket (10 );

t .start ();//如果使用run()方法,将會執行完t才能執行t2;

t2 .start ();

           }

/*      第1号火箭發射倒計時:7

          第1号火箭發射倒計時:6

          第1号火箭發射倒計時:5

          第1号火箭發射倒計時:4

          第1号火箭發射倒計時:3

          第1号火箭發射倒計時:2

          第2号火箭發射倒計時:7

          第1号火箭發射倒計時:1

          第2号火箭發射倒計時:6*/

} 示例中發現:建立并啟動一個線程,用start()。調用線程的start方法,啟動線程,調用run方法。這是因為線程啟動需要調動底層資源。

發現運作結果每次都不同,因為每個線程都在擷取cpu的執行權,cpu執行到誰,誰就運作。cpu在做着快速的切換已達到看上去是同時運作的結果,這就是多線程的一個特性,随機性誰搶到誰執行,至于執行多長,cpu說了算。

為什麼要覆寫run方法。

thread用于描述線程。該類就定義了一個功能,用于存儲線程要運作的代碼,該存儲功能就是run方法,run方法用于存儲線程運作的代碼,主線程放在main裡面。你開啟線程的意義就是為了執行你指定的代碼。複寫run方法的目的就讓那個自定義的代碼存儲在run方法中,讓程序運作。不能d.run,調用的run方法,主線程執行run,start沒有開啟線程。start開啟線程,執行該線程的run方法,run就是一般調用方法,還是單線程的。

線程的運作狀态。

被建立(調用windows)start開始運作,當機(睡眠時間)時間結束回到運作wait()等待。notify喚醒功能。任務管理器的結束程序,才結束。放棄執行資格。消亡:stop和run方法結束。阻塞狀态:建立了不一定就馬上能執行,要等待cpu來執行,具備運作資格,但沒有執行權。當機狀态也是回到阻塞狀态。

擷取線程對象以及名稱

線程都有自己預設的名稱,編号從0開始。(用super改寫名稱)currentthread()擷取目前線程對象

getname()擷取線程名稱。

局部變量在每一個線程中都有自己獨立的空間。

賣票程式

同時出票。

總共隻有100張票,你建立了4個,賣了400張。。。用static可以,但是生命周期太長。t1start4次,會出現程序運作。跑圈,前面每跑一圈bang一槍。你已經到了運作狀态,再運作沒有意義。這也是不ok的,在運作的線程不需要開啟。因為沒有方法了,因為繼承Thread類的run方法存放在類Ticket中

實作runable類。必須實作無參run方法。

實作runnable,實作run

調用:

ticket t=new

t不是線程newthread才是線程飯

用多态指向ticket。在建立線程對象時就要明确運作那段代碼

實作thread

thread裡可以傳ranable的對象,他有這樣一個構造函數。實作runable接口

步驟:

1.定義類實作runable接口

2.覆寫runable裡run方法

3.通過thread類建立線程對象。

4.ranable接口的子類對象作為實際參數傳遞給threa類的構造函數

 因為自定義的run方法所屬對象是runable接口的子類對象,是以要讓線程去執行制定對象的run方法。就必須明确該run方法所屬對象。

5.開啟start并調用runnable的子類對象run;

/*Runnable

run=new Rocket2(20);//雖然實作了Runnable方法,但是不能調用start

Runnable

run2=new Rocket2(20);*///說明start方法是在Thread類裡面的,不再Runnable中

Rocket2 r =new Rocket2 (10 ); //不能用Thread的引用指向Rocket

Rocket2 r2 =new Rocket2 (10 );

Thread t =new Thread (r );

Thread t2 =new Thread (r2 );

t .start ();

/*      第2号火箭發射倒計時:7

          第2号火箭發射倒計時:6

          第2号火箭發射倒計時:5*/

另外如果對同一個啟動多次會抛出異常

//throw

new IllegalThreadStateException();

實作方式和繼承方式的差別:

單繼承。方框裡的代碼需要多線程去執行。學生類繼承了person,有了父類,但是你要多線程執行,怎麼辦。我可以給你提供方法,但是你要符合我的規則。學生是人的一種,學生象runnable

實作方式的好處是避免了但繼承的局限性,在定義線程時用實作的方式,而且這方式也可以共享t對象。。。。runnable。

;兩種方式最大的不同就是線程代碼存放位置的不同,繼承存放在thread子類的run方法中,實作時,線程代碼存放在接口的子類的run方法中。

 安全問題

就是那個判斷了,沒執行的問題。多語句的時候,同時又操作了共享資料時,寫多程序的時候一定要小心安全問題,否則有時測試不出來,但是中病毒了呢,問題的原因在于多條語句在在操作同一個線程的共享資料時,一個線程對多條語句隻執行了一部分還沒有執行完,另一條線程參與進來執行,導緻共享資料的錯誤。

同步代碼塊

synchronization哪些要同步呢?操作共分享資料的語句。

同步代碼塊的原理:同步鎖。。某段時間類隻能由一條線程,直到這段代碼執完畢。持有鎖的線程可以在同步中執行,沒有持有所得線程即使擷取cpu的執行權也進不去,因為沒有擷取鎖。火車上的衛生間

同步書寫的前提,1。必須要有兩個或者連着以上的線程

 2.必須是多個線程 使用同一個鎖,才能叫同步。必須保證同一段代碼隻有一個線程運作,

好處解決了多線的安全問題

弊端:多了一個判斷是否上鎖,消耗了資源,是在允許消耗的範圍内。

同步函數。

1.明确哪些代碼是多線程運作代碼

2.明确共享資料

3.明确多線程運作代碼中那些語句是操作共享資料的。

同步封裝代碼和函數封裝的差別:帶有同步性與否。

把synchronized,作為修飾符放在函數上。兩種表現形式同步代碼塊和同步函數。

同步函數的鎖是this

怎麼隻有0線程在執行?因為循環鎖住了。别人進不來。。。沒搞清楚哪些藥同步哪些不用,拿出來,調用show就好了。同步函數用得是哪一個鎖呢?函數需要被對象調用。怎麼驗證這結論:使用兩個線程來賣票,一個線程在同步代碼塊中,一個同步函數,都在執行賣票工作為什麼都在run裡執行,不執行code?哦,主函數太快了,讓主函數睡一會兒。還是出現了錯票0

。不安全。兩個鎖不一樣。

練習:不要在寫一個函數然後又加任何類名。

靜态同步函數的是class對象。靜态進記憶體時,記憶體中沒有本類對象,但是一定有該類對應的位元組碼檔案對象,Ticket.class。靜态的同步方法使用的鎖是所在類的位元組碼檔案對象。類名.class。懶漢式加了鎖會比較低效,因為每一個都要判斷用雙重否定。

懶漢式

死鎖:同步中嵌套同步而鎖卻不同。盡量避免死鎖。