多線程基礎知識
程序是一個正在執行的程式。
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。懶漢式加了鎖會比較低效,因為每一個都要判斷用雙重否定。
懶漢式
死鎖:同步中嵌套同步而鎖卻不同。盡量避免死鎖。