標籤:多線程
進程與線程
進程:就是進行中的程式。其實就是一個應用程式運行時的記憶體空間。
線程:線程就是進程當中的一個控制單元或執行路徑。進程負責空間的標示,而線程負責執行應用程式的執行順序。
當一個進程中出現多個線程是就是多線程。每個線程在棧中都有自己的執行空間、方法區、變數。
java VM啟動的時候會有一個進程java.exe。該進程中至少有一個線程負責java程式的執行,而且這個線程啟動並執行代碼存在於main方法中。該線程稱之為主線程。
線程的建立方法
建立線程中第一種方式:繼承Thread類。
步驟:
1.定義類繼承Thread類。
2.複寫Thread類中run方法
目的:將自訂代碼儲存在run方法,讓線程運行。
3.調用線程start方法。
該方法有兩個作用:啟動線程,調用run方法。
為什麼覆蓋run方法?
Thread類用於描述線程。該類定義了一個功能,用於儲存線程要啟動並執行代碼。該儲存功能就是run方法。
也就是說Thread類中run方法,用於儲存線程要啟動並執行代碼。
線程都有自己的名稱:Thread-編號 該編號從0開始,。
static Thread currentThread();擷取當前線程對象。
getName()擷取線程名稱。
設定線程名稱: setName()或者建構函式。
代碼執行個體:
class Demo extends Thread
{
public void run()
{
for(int x=0; x<60; x++)
System.out.println("demo run----"+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo();//建立好一個線程。
d.start();//開啟線程並執行該線程的run方法。
//d.run();//僅僅是對象調用方法。而線程建立了,並沒有運行。
for(int x=0; x<60; x++)
System.out.println("Hello World!--"+x);
}
}
第二種方式:實現Runable介面
步驟:
1.定義類實現Runable介面
2.覆蓋Runable介面中的run方法。
將線程啟動並執行代碼儲存在run方法中。
3.通過Thread類建立線程對象。
4.將Runable介面的子類對象作為實際參數傳遞給Thread類的建構函式。Thread(Runable r)
自訂的run方法所屬的對象是Runable介面的子類對象。所以要讓線程去運行指定對象的run方法。就必須明確該run方法所屬的對象。
5.調用Thread類的start方法開啟線程(調用Runable介面子類中的run方法。)
售票執行個體:
class Ticket implements Runnable//extends Thread
{
private int tick = 100;
public void run()
{
while(true)
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//建立了一個線程;
Thread t2 = new Thread(t);//建立了一個線程;
Thread t3 = new Thread(t);//建立了一個線程;
Thread t4 = new Thread(t);//建立了一個線程;
t1.start();
t2.start();
t3.start();
t4.start();
}
}
兩種方式的區別:
繼承Thread:線程代碼存放Thread子類的run方法中。
實現Runable:線程代碼存放在介面的子類的run方法。
實現方式避免了單繼承的局限性,在定義線程時,建議使用實現方式。
線程狀態:
1.被建立:等待啟動,調用start啟動。
2.運行狀態:具有執行資格和執行權。
3.臨時狀態(阻塞):有執行資格,但是沒有執行權。
4.凍結狀態:遇到sleep(time)方法和wait()方法時,失去執行資格和執行權,sleep方法時間到或者調用notify()方法時,獲得執行資格,變為臨時狀態。
5. 消忙狀態:stop()方法,或者run方法結束。
多安全執行緒問題:一個線程在運行多條語句、操作同一個資料的時候,其他線程參與進來。導致錯誤資料的產生。
解決辦法:只要讓共用資料在某一時間只由一個線程執行,在此過程中其他的不能執行。所以會用到同步 Sychronized(對象){需要被同步的代碼}。
對象如同鎖,持有鎖的線程可以在同步中執行,沒有持有鎖的線程即使擷取CPU的執行權,也進不去,因為沒有擷取鎖
同步函數:用synchronized修飾函數即可。
鎖是this。若同步函數被static修飾,所屬類.class。
同步的前提:
必須有兩個或兩個以上線程才需要同步
多個線程必須用同一個鎖。
同步的好處是解決的安全執行緒問題,弊端是需要不斷的判斷鎖,消耗資源。
多線程間通訊和一些線程操作方法
線程間通訊其實就是多個線程在操作同一個資源,但是操作的動作不同
需要使用的方法有:
wait();
notify();
notifyAll();
上面的方法都使用在同步中,因為要對持有監視器(鎖)的線程操作,所以要使用在同步中,因為只有同步才具有鎖
為什麼這些操作線程的方法要定義在Object類中呢?
因為這些方法在操作同步中線程時,都必須要標識它們所操作線程持有的鎖,只有同一個鎖上的被等待線程,可以被同一個鎖上的notify喚醒,不可以對不同鎖中的線程進行喚醒,也就是說,等待和喚醒必須是同一個鎖,而鎖可以是任意對象,所以可以被任意對象調用的方法定義在Object類中。
同步中的死結問題
何時會出現死結?當同步中嵌套同步時會出現死結。 這時會出現一個線程持有a鎖要想執行下去要擷取b鎖,而另一個線程持有b鎖要想執行下去要擷取a鎖。這就成了兩個線程各自持有一個鎖,又要擷取對方的鎖,這時就出現了死結。程式會定在那執行不下去。
停止線程
只有一種,run方法結束。開啟多線程運行,運行代碼通常是迴圈結構。只要控制住迴圈,就可以讓run方法結束,也就是線程結束。
特殊情況:
當線程處於了凍結狀態。就不會讀取到標記。那麼線程就不會結束。當沒有指定的方式讓凍結的線程恢複到運行狀態時,這時需要對凍結進行清除。強制讓線程恢複到運行狀態中來。這樣就可以操作標記讓線程結束。Thread類提供該方法 interrupt();
本文出自 “點點滴滴” 部落格,請務必保留此出處http://arctictern.blog.51cto.com/10120640/1659840
黑馬程式員-----Java基礎之多線程