黑馬程式設計師_java多執行緒
多執行緒
1 建立執行緒
方式一:繼承Thread類,覆蓋run方法。
步驟:1、定義類繼承Thread。
2、複寫Thread類中的run方法。
3、呼叫執行緒的start方法,該方法兩個作用:啟動執行緒,呼叫run方法。
package cn.itcast.thread; //寫一個類繼承Thread,模擬單執行緒的 class Demo extends Thread{ Demo(String name){ super(name); } public void run(){ for(int x = 0 ; x < 10;x++){ System.out.println("demoRun..."+x+getName()); } } } public class ThreadDemo { public static void main(String[] args){ Demo d = new Demo("旺財"); d.start(); for(int x = 0 ; x < 10 ; x++){ System.out.println("main..."+x+Thread.currentThread().getName()); } } }
發現執行結果每次都不同。
因為多個執行緒都獲得cpu的執行權,cpu執行到誰,誰就執行。
明確一點,在某一個時刻,只能有一個執行緒在執行,cpu咋做著快速的切換,已達到看上去是同時執行的效果。
我們可以形象把多執行緒的執行行為在互相搶奪cpu的執行權。
這就是多執行緒的一個特性:隨機性,誰搶到誰執行,至於執行多長,cpu說了算。
為什麼要覆蓋run方法呢?
Thread類用於描述執行緒。該類就定義了一個功能,用於儲存執行緒要執行的程式碼,該儲存功能就是run方法。
也就是說Thread類中的run方法,用於儲存執行緒要執行的程式碼。
執行緒預設名稱,this.getName(),是從0開始。
用繼承Thread的建立多執行緒模擬售票
package cn.itcast.thread; /* * 利用多執行緒,實現模擬多視窗同時售票 */ //定義類,票,模擬售票 class Ticket extends Thread{ private static int tickets = 100; Object obj = new Object(); public void run(){ while(true){ synchronized (obj) { if(tickets>0){ try{ Thread.sleep(5); }catch(Exception e){} System.out.println(getName()+"..出售第"+tickets--+"張票"); } } } } } public class TicketDemo { public static void main(String[] args) { Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); Ticket t3 = new Ticket(); Ticket t4 = new Ticket(); t1.start(); t2.start(); t3.start(); t4.start(); } }
方式二:實現Runable介面
步驟:1,定義類實現Runable介面。
2,覆蓋Runable介面中的run方法。
3,通過Thread類建立執行緒物件。
4,將Runable介面的子類物件作為實際引數傳遞給Thread的建構函式。
5,呼叫Thread類的start方法開啟執行緒並呼叫Runable介面子類的run方法。
package cn.itcast.thread;
//寫一個類實現Runnable,模擬單執行緒的
class Demo implements Runnable{
public void run(){
for(int x = 0 ; x < 10;x++){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("demoRun..."+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args){
Demo d = new Demo();
Thread t = new Thread(d);
t.start();
for(int x = 0 ; x < 10 ; x++){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main..."+x+Thread.currentThread().getName());
}
}
}
實現方式和繼承方式有什麼不同?
實現方式的好處:避免了單繼承的侷限性。在定義執行緒時,建議使用實現方式。
繼承Thread:執行緒程式碼存放Thread子類run方法中。
實現Runable,執行緒程式碼存在介面的子類的run方法中。
執行緒的五種狀態:
建立:start()
臨時阻塞:CPU在執行某個執行緒時,其它執行緒都不會執行,具備執行資格,但是沒有執行權
凍結:sleep()或wait(),時間到notify()
執行:run()或stop()
消亡:run()方法結束
sleep()需要指定睡眠時間,單位是毫秒;
特殊的狀態:臨時阻塞。具備了執行資格,但是還沒有獲取資源;
2 解決執行緒安全問題
解決原理:讓多條操作共享資料的程式碼在某一時間段被一個執行緒執行,執行過程中其它執行緒不參與運算;
同步程式碼塊: synchronized(物件) {
需要同步的程式碼;
}
將訪問資料的程式碼鎖住,在同步程式碼塊中的內容同一時間內只能一個執行緒執行;
同步可以解決安全問題的根本原因就在那個物件上;該物件如同鎖的功能;
同步方法:方法上加synchronized,整個方法的程式碼都是同步的,只能一個執行緒執行,同步方法使用this作為鎖;
同步的前提:要兩個或者兩個以上的執行緒;多個執行緒使用的是同一個鎖;
同步的好處:同步的出現解決了多執行緒的安全問題;
同步的弊端:當執行緒相當多時,每個執行緒都會去判斷同步上的鎖耗費資源,降低程式的執行效率;
死鎖:在多個執行緒併發執行使用多個鎖來同步時,有可能互相沖突,導致程式無法繼續執行(同步巢狀);
同步出現解決了執行緒安全問題,但是非常消耗資源,效率會降低並且容易引發死鎖;
3 執行緒間通訊
在同步程式碼中可以使用鎖物件的wait()方法讓當前執行緒等待;
使用鎖物件的notify()方法可以將正在等待的執行緒喚醒;
如果多個執行緒都在等待,notify()喚醒隨機1個;
notifyAll()方法可以喚醒所有在等待的執行緒;
wait(),notify(),notifyAll(),這些用來操作執行緒存在與同步中,使用這些方法時必須要標識所屬的同步的鎖,
鎖可以是任意物件,所以任意物件呼叫的方法一定定義Object類中;
wait(),sleep()區別:
wait():釋放cpu執行權,釋放鎖;
sleep():釋放cpu執行權,不釋放鎖;
4 JDK5之後的執行緒同步與通訊
同步:
使用java.util.concurrent.locks.Lock介面的實現類物件來進行同步;
ReentrantLock就是Lock的實現類,可以實現synchronized的功能;
在需要同步的程式碼塊前後使用lock()和unlock()方法來完成同步;
unlock()最好放在finally中,因為如果上面程式碼丟擲異常沒有解鎖;,會導致其它執行緒無法執行,程式卡死;
通訊:
使用Lock物件的newCondition()方法獲取一個Condition物件,Condition物件可以控制指定執行緒的等待與喚醒;
await()方法可以控制執行緒等待;
signal()方法可以喚醒等待的執行緒;
signalAll()方法可以喚醒所有等待執行緒;
5 停止執行緒
定義迴圈結束標記:因為執行緒執行程式碼一般都是迴圈,只要控制了迴圈即可;
使用interrupt()中斷:該方法是結束執行緒的凍結狀態,使執行緒回到執行狀態中來;
注:stop()已經過時不再使用;
6 執行緒中方法
currentThread():靜態方法,返回當前執行緒物件的引用;
getName(),setName():用來獲取、設定當前執行緒的名字;
sleep():控制執行緒休眠,單位為毫秒;
setDaemon():將執行緒設定為守護執行緒。執行緒預設是非守護執行緒,守護執行緒不能單獨執行;
join():讓出執行權,當前執行緒暫停,等待加入的執行緒執行結束,當前執行緒再執行;
setPriority(int num):設定執行緒優先順序;
toString():返回該執行緒的字串表示形式,包括執行緒名稱、優先順序和執行緒組;
Thread(String name):建構函式,執行緒物件一建立就可以指定名稱;
setName(String name):自定義執行緒名字
------- android培訓、java培訓、java學習型技術部落格期待與您交流! ----------