標籤:預設 art == 一段 getname system dem 線程的生命週期 ++
1.兩種方式的差異2.線程的生命週期3.線程式控制制(線程的方法)4.線程同步5.線程同步鎖一、 兩種方式的差異A extends Thread :簡單不能再繼承其他類了(Java單繼承)同份資源不共用B implements Runnable:( 推薦) )多個線程共用一個目標資源,適合多執行緒同一份資源。該類還可以繼承其他類,也可以實現其他介面。二、 線程的生命週期建立:當程式使用new建立一個線程後,該線程處於建立狀態,此時他和其他java對象一樣,僅僅由Java虛擬機器為其分配記憶體並初始化成員變數值。【 Thread r = new Thread()】就緒:當線程對象調用start()方法後,該線程處於就緒狀態,線程計入線程隊列排隊,此時該狀態線程並未開始執行,它僅表示可以運行了。至於該線程何時運行,取決於JVM線程調度器的調度。【 r.start() 】運行:若處於就緒狀態的線程獲得了CPU,開始執行run()線程執行體,該線程處於執行狀態。阻塞:線程運行過程中需要被中斷,目的是是其他的線程獲得執行的機會。該狀態就會進入阻塞狀態。注意:阻塞狀態不能直接轉成運行狀態,阻塞狀態只能重新進入就緒狀態。死亡:run()執行完成,線程正常結束;線程拋出未捕獲的Exception或Error;調用線程的stop()。(易導致死結,不推薦)注意:主線程結束後,其他線程不受其影響,不會隨之結束;一旦子線程啟動起來後,就擁有和主線程相等地位,不受主線程影響。例子:class MyThread implements Runnable { public void run() { for (int i = 0; i < 200;i++) { System.out.println(i); } }}public class MyThreadDemo { public static void main(String[] args) { for (int i = 0; i < 10;i++) { if(i == 5){ MyThread t = new MyThread(); new Thread(t).start(); } System.out.println("main" + i); } }}測試線程是否活著,可用線程對象的isAlive()方法。當線程處於就緒,運行,阻塞狀態返回true。當線程處於建立和死亡狀態,返回false。已死亡的線程是不可能通過start()方法喚醒線程的。否則引發IllegalThreadStateException異常;public class DieThreadDemo { public static void main(String[]args) { DeadThread dt = new DeadThread(); for (int i = 0; i < 100; i++) { Thread mainThread =Thread.currentThread();//獲得當前線程(Main線程) System.out.println(mainThread.getName()+ "--" + i); System.out.println("------>"+mainThread.isAlive()); if (i == 10) { dt.start(); } } if (!dt.isAlive()) { dt.start(); } }}class DeadThread extends Thread { public void run() { for (int i = 0; i < 50; i++) { System.out.println(getName() +"--" + i); } }}三、線程式控制制Join()方法:等待該線程終止。join 方法:調用join方法的線程對象強制運行,該線程強制運行期間,其他線程無法運行,必須等到該線程結束後其他線程才可以運行。有人也把這種方式成為聯合線程join方法的重載方法:join(long millis):join(long millis,int nanos):通常很少使用第三個方法:程式無須精確到一納秒;電腦硬體和作業系統也無法精確到一納秒;public class JoinDemo { public static void main(String[]args) throws Exception { Thread join = new Thread(newJoin(),"Join線程"); join.start(); int i = 0; while(i < 500){ if(i == 100){ join.join(); } System.out.println("Main -- > "+ i ++); } }}class Join implements Runnable { public void run() { int i = 0; while(i< 200){ System.out.println(Thread.currentThread().getName() + "--" + i ++ ); } }}setDaemon()方法後台線程:處於後台運行,任務是為其他線程提供服務。也稱為“守護線程”或“精靈線程”。JVM的記憶體回收就是典型的後台線程。特點:若所有的前台線程都死亡,後台線程自動死亡。設定後台線程:Thread對象setDaemon(true);setDaemon(true)必須在start()調用前。否則出現IllegalThreadStateException異常;前台線程建立的線程預設是前台線程;判斷是否是後台線程:使用Thread對象的isDaemon()方法;並且若且唯若建立線程是後台線程時,新線程才是後台線程。class Daemon extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println(getName() +"-" + i); } }}public class DaemonDemo { public static void main(String[]args) { Daemon d = new Daemon(); d.setDaemon(true);//把 d 線程設定成後台線程 d.start(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+"--" + i); } }}Sleep()線程休眠:讓執行的線程暫停一段時間,進入阻塞狀態。sleep(long milllis) throws InterruptedException:毫秒sleep(long millis,int nanos) throws InterruptedException:毫秒,納秒調用sleep()後,在指定時間段之內,該線程不會獲得執行的機會。public class SleepDemo { public static void main(String[]args) { for (int i = 10; i > 0; i--) { System.out.println("還剩" + i); try { Thread.sleep(1000); } catch (InterruptedException e){ e.printStackTrace(); } } }}四、線程優先順序每個線程都有優先順序,優先順序的高低只和線程獲得執行機會的次數多少有關。並非線程優先順序越高的就一定先執行,哪個線程的先運行取決於CPU的調度;預設情況下main線程具有普通的優先順序,而它建立的線程也具有普通優先順序。Thread對象的setPriority(int x)和getPriority()來設定和獲得優先順序。MAX_PRIORITY : 值是10MIN_PRIORITY : 值是1NORM_PRIORITY : 值是5(主方法預設優先順序)class Priority extends Thread{ public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName()+",優先順序=" + getPriority() +"--"+ i); } }}public class PriorityDemo { public static void main(String[]args) { Thread.currentThread().setPriority(7); for (int i = 0; i < 100; i++) { if(i == 10){ Priority p1 = new Priority(); p1.setPriority(Thread.MAX_PRIORITY); p1.start(); } if(i == 15){ Priority p2 = new Priority(); p2.setPriority(Thread.MIN_PRIORITY); p2.start(); } System.out.println("main" + i); } }}Yield()方法線程禮讓:暫停當前正在執行的線程對象,並執行其他線程;Thread的靜態方法,可以是當前線程暫停,但是不會阻塞該線程,而是進入就緒狀態。所以完全有可能:某個線程調用了yield()之後,線程調度器又把他調度出來重新執行。class Yield implements Runnable { public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--> " + i); if (i % 2 == 0) { Thread.yield();// 禮讓 } } }}public class YieldDemo { public static void main(String[]args) { Yield y = new Yield(); new Thread(y, "A").start(); new Thread(y, "C").start(); }}API 過時方法 -- 易死結, , 不推薦使用stop:終止線程馬上讓線程停止運行,並釋放該線程所持有的鎖,該操作無法保證對象的內部狀態正確;suspend:掛起線程使線程進入“阻塞”狀態,該狀態下CPU不會分給線程時間片,進入這個狀態可以用來暫停一個線程的運行,在被resume方法調用前,不可用.如果要suspend的目標線程對一個重要的系統資源持有鎖,那麼沒任何線程可以使用這個資源直到要suspend的目標線程被resumed。resume:恢複線程恢複被suspend方法掛起的線程Java2開始已經廢棄了suspend()和resume()方法,因為使用這兩個方法可能會產生死結,所以應該使用同步對象調用wait()和notify()的機制來代替suspend()和resume()進行線程式控制制。五、線程的安全性問題導致安全問題的出現的原因:多個線程訪問出現延遲。線程隨機性。註:安全執行緒問題在理想狀態下,不容易出現,但一旦出現對軟體的影響是非常大的。我們可以通過Thread.sleep(long time)方法來簡單類比延遲情況。六、線程同步當多個線程訪問同一份資料的時候,很容易出現安全執行緒的問題。讓多個操作在同一個時間段內只能有一個線程進行,其它線程要等到該線程完成後才可以繼續執行。經典的案例: 銀行取錢使用者輸入取款金額;系統判斷餘額是否大於取款金額若餘額大於取款金額,取款成功;若餘額小於取款金額,取款失敗。例子分析:賬戶類:Account欄位balance表示餘額取錢類:DrawMoney 線程類欄位a表示賬戶對象,money表示需要取的金額;把取錢行為放線上程體裡面;提示:public void run(){ if(c.getBlance() >= money){ System.out.println(getName()+",取走" + money); Thread.sleep(100); c.setBlance(c.getBlance()- money); System.out.println("餘額= " +c.getBlance()); }else{ System.out.println(getName()+ "餘額不足,餘額"+ c.getBlance() +",需要取走" +money ); }}解決問題:方法一:當發生以上情況時,java給我們提供了同步代碼塊synchronized(obj){//obj表示同步監視器,是同一個同步對象/**.....TODO SOMETHING*/}方法二:synchronized 傳回值類型 方法名(參數列表){/**.....TODO SOMETHING*/}同步方法的同步監聽器其實的是 this/*** 帳號**/public class Account {/*** 餘額*/ private double blance; public Account(double blance) { this.blance = blance; } public double getBlance() { return blance; } public void setBlance(double blance){ this.blance = blance; } public synchronized void draw(doubledrawMoney){ if(getBlance() >= drawMoney) { System.out.println(Thread.currentThread().getName()+",取出" + drawMoney); try { Thread.sleep(1); } catch (InterruptedException e){ e.printStackTrace(); }//修改餘額 setBlance(getBlance() -drawMoney); System.out.println("餘額= " +getBlance()); }else{ System.out.println("對不起餘額不足"); } }}/*** 取錢操作*/public class DrawThread extendsThread{/*** 帳號*/ private Account a;/*** 取的錢*/ public double drawMoney; public DrawThread(String name,Account a,double drawMoney){ super(name); this.a = a; this.drawMoney = drawMoney; }/*** 當多條線程修改同一個共用資源資料的時候,將會導致資料安全問題*/ public void run(){/* synchronized (a) { if(a.getBlance() >= drawMoney) { System.out.println(getName()+",取出" +drawMoney); try { Thread.sleep(1); } catch (InterruptedExceptione) { e.printStackTrace(); }//修改餘額 a.setBlance(a.getBlance() -drawMoney); System.out.println("餘額= " +a.getBlance()); }else{ System.out.println("對不起餘額不足"); } }*/ a.draw(drawMoney); }}以上內容是課堂筆記之後是今天作業:做一個售票的程式要求三個視窗,一共50張票,隨機售票,售完後求小明在每個視窗購票的幾率:
public class Dome { //主類
public static void main(String[] args) { //主方法
Chuangkou c1 = new Chuangkou("視窗一");
Chuangkou c2 = new Chuangkou("視窗二");
Chuangkou c3 = new Chuangkou("視窗三");
Thread t1 = new Thread(c1); //建立三個線程
Thread t2 = new Thread(c2);
Thread t3 = new Thread(c3);
t1.start(); //三個線程同時運行
t2.start();
t3.start();
}
}
public class Piao{ //用來統計票數
public static String name = "目前只有這一種票";
public static int piao = 1;
}
public class Chuangkou implements Runnable{ //視窗類別
public String name;
static int a = 0; //定義三個靜態變數用來統計每個視窗一共賣的票
static int b = 0;
static int c = 0;
public Chuangkou(String name) { //有參構造器
this.name = name;
}
public void run(){ //run方法
while(Piao.piao<49){ //如果票數等於49就停止
try {
Thread.sleep(1/10); //經測試睡眠1/10的時間最有利於每個視窗的售票率
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(Piao.name){ //同步,防止多個線程同時運行
System.out.println(name+"賣出了第 "+Piao.piao+++"票");
}
switch(name){ //統計視窗票數
case "視窗一":a+=1;break;
case "視窗二":b+=1;break;
case "視窗三":c+=1;break;
}
}
System.out.println(name+"售罄");
if(Piao.piao == 50){ //計算出小明購票幾率
System.out.println("小明買視窗一的票的機率為"+(a/50.0));
System.out.println("小明買視窗二的票的機率為"+(b/50.0));
System.out.println("小明買視窗三的票的機率為"+(c/50.0));
}
}
}
目前這個作業還有問題,不知為什麼小明的效率有時會出現兩次,有時不出現,希望大神指導一下。
線程(java課堂筆記)