Java執行緒: 執行緒排程
一、休眠
休眠的目的是使執行緒讓出CPU的最簡單做法,執行緒休眠的時候,會將CPU交給其他執行緒,以便輪換執行,休眠一定時間後,執行緒會甦醒,進入準備狀態等待執行。執行緒的休眠方法是Thread.sleep(long millis)和Thread.sleep(long millis,int nanos),均為靜態方法,呼叫sleep休眠的哪個執行緒呢?哪個執行緒呼叫sleep,哪個執行緒休眠。例子:
執行緒1休眠後,讓出CPU,執行緒2執行,執行緒2執行完後,執行緒2休眠,讓出CPU供執行緒1執行,(此時執行緒1已經休眠結束,在等待狀態),如此迴圈執行,直到結束。
SleepTest.java
1 package Thread; 2 3 public class SleepTest { 4 public static void main(String[] args){ 5 Thread t1=new MyThread_1(); 6 Thread t2=new Thread(new MyRunnable1()); 7 t1.start(); 8 t2.start(); 9 } 10 } 11 class MyThread_1 extends Thread{View Code12 public void run(){ 13 for(int i=0;i<3;i++){ 14 System.out.println("執行緒1第"+i+"次執行!"); 15 try{ 16 Thread.sleep(500);//影響的是執行速度。 17 }catch(InterruptedException e){ 18 e.printStackTrace(); 19 } 20 } 21 }22 } 23 class MyRunnable1 implements Runnable{ 24 public void run(){ 25 for(int i=0;i<3;i++){ 26 System.out.println("執行緒2第"+i+"次執行!"); 27 try{ 28 Thread.sleep(500); 29 }catch(InterruptedException e){ 30 e.printStackTrace(); 31 } 32 } 33 } 34 }
結果為:
1 執行緒1第0次執行! 2 執行緒2第0次執行! 3 執行緒1第1次執行! 4 執行緒2第1次執行! 5 執行緒2第2次執行! 6 執行緒1第2次執行!
二、執行緒優先順序
可以用setPriority方法提高或降低任何一個執行緒的優先順序,具體為1-10之間的數,預設優先順序為5。並非優先順序低的執行緒沒有機會執行,優先順序高低只是代表了執行機會的大小。每一個執行緒都有一個優先順序。一個執行緒繼承它父執行緒的優先順序,它們的優先順序相同。例子:
PriorityTest.java
1 package Thread; 2 public class PriorityTest { 3 public static void main(String[] args){ 4 Thread t1=new MyThread_2(); 5 Thread t2=new Thread(new MyRunnable2()); 6 t1.setPriority(10); 7 t2.setPriority(1); 8 t2.start(); 9 t1.start(); 10 11 } 12 } 13 class MyThread_2 extends Thread{ 14 public void run(){ 15 for(int i=0;i<10;i++){ 16 System.out.println("執行緒1第"+i+"次執行!"); 17 try{ 18 Thread.sleep(100);//影響的是執行速度。 19 }catch(InterruptedException e){ 20 e.printStackTrace(); 21 } 22 } 23 } 24 } 25 class MyRunnable2 implements Runnable{ 26 public void run(){ 27 for(int i=0;i<10;i++){ 28 System.out.println("執行緒2第"+i+"次執行!"); 29 try{ 30 Thread.sleep(100); 31 }catch(InterruptedException e){ 32 e.printStackTrace(); 33 } 34 } 35 } 36 }View Code
結果為:
1 執行緒1第0次執行! 2 執行緒2第0次執行! 3 執行緒1第1次執行! 4 執行緒2第1次執行! 5 執行緒1第2次執行! 6 執行緒2第2次執行! 7 執行緒1第3次執行! 8 執行緒2第3次執行! 9 執行緒1第4次執行! 10 執行緒2第4次執行! 11 執行緒1第5次執行! 12 執行緒2第5次執行! 13 執行緒2第6次執行! 14 執行緒1第6次執行! 15 執行緒1第7次執行! 16 執行緒2第7次執行! 17 執行緒1第8次執行! 18 執行緒2第8次執行! 19 執行緒1第9次執行! 20 執行緒2第9次執行!View Code
三、守護執行緒
呼叫t.setDaemon(true)將執行緒轉換成守護執行緒。守護執行緒的唯一用途是為其他執行緒提供服務。比如說,JVM的垃圾回收、記憶體管理等執行緒都是守護執行緒。計時執行緒就是一個例子,它定時的傳送“計時器滴答”訊號給其他執行緒或清空過時的快取記憶體項的執行緒,最後只剩下守護執行緒時,JVM就退出了。
setDaemon方法的詳細說明:
1 public final void setDaemon(boolean on)將該執行緒標記為守護執行緒或使用者執行緒。當正在執行的執行緒都是守護執行緒時,JVM退出。 2 該方法首先呼叫該執行緒的checkAccess方法,不帶任何引數,可能丟擲SecurityException(在當前執行緒中)。 3 4 引數: 5 on-如果為true,則將該執行緒標記為守護執行緒。 6 丟擲: 7 IllegalThreadStateException-如果該執行緒處於活動狀態。 8 SecrurityException-如果當前無法修改該執行緒。 9 另請參見: 10 isDaemon(),checkAccess()
具體例子:當最後只剩下守護執行緒在執行時,JVM退出。
DaemonTest.java
1 package Thread; 2 public class PriorityTest { 3 public static void main(String[] args){ 4 Thread t1=new MyThread_2(); 5 Thread t2=new Thread(new MyRunnable2()); 6 t2.setDaemon(true);//設定為守護執行緒 7 t2.start(); 8 t1.start(); 9 10 } 11 } 12 class MyThread_2 extends Thread{ 13 public void run(){ 14 for(int i=0;i<5;i++){ 15 System.out.println("執行緒1第"+i+"次執行!"); 16 try{ 17 Thread.sleep(7);//影響的是執行速度。 18 }catch(InterruptedException e){ 19 e.printStackTrace(); 20 } 21 } 22 } 23 } 24 class MyRunnable2 implements Runnable{ 25 public void run(){ 26 for(int i=0;i<99999L;i++){ 27 System.out.println("執行緒2第"+i+"次執行!"); 28 try{ 29 Thread.sleep(7); 30 }catch(InterruptedException e){ 31 e.printStackTrace(); 32 } 33 } 34 } 35 }View Code
結果為:
1 執行緒2第0次執行! 2 執行緒1第0次執行! 3 執行緒1第1次執行! 4 執行緒2第1次執行! 5 執行緒1第2次執行! 6 執行緒2第2次執行! 7 執行緒1第3次執行! 8 執行緒2第3次執行! 9 執行緒1第4次執行! 10 執行緒2第4次執行! 11 執行緒2第5次執行!View Code
四、未捕獲異常處理器
run方法不能丟擲任何被檢測的異常,但是,不被檢測的異常就會導致執行緒的終止。但是不需要任何catch字句來處理被傳播的異常。相反,死亡之前,異常被傳遞到一個用於未捕獲異常處理器。該處理器實現一個Thread.UncaughtExceptionHandler介面的類。這個介面只有一個方法即:void uncaughtException(Thread t,Throwable e)
用setUncaughtaExceptionHandler方法為任何執行緒安裝一個處理器。但是如果不為此獨立的執行緒安裝處理器,則ThreadGroup類物件即為此時的處理器。
ThreadGroup類實現了Thread.UncaughtExceptionHandler介面。它的uncaughtException方法做如下操作:
1) 如果該執行緒組有父執行緒組,那麼父執行緒組的uncaughtException方法被呼叫。
2) 否則,如果Thread.getDefaultExceptionHandler方法返回一個非空的處理器,則呼叫該處理器。
3) 否則,如果Throwable是ThreadDeath的一個例項,什麼都不做。
4) 否則,執行緒的名字以及Throwable的棧軌跡被輸出到System.err上。此時可以看到多次的棧軌跡。
五、讓步
讓步就是使當前執行的執行緒讓出CPU資源,雖然不知道給誰,僅僅是讓出,執行緒狀態回到可執行狀態。其中讓步使用Thread.yield()方法,yield方法為靜態方法,功能是暫停當前執行的執行緒物件,並執行其他執行緒。例子:一個執行緒先讓步,讓另一個執行緒先執行,然後再執行該執行緒。
YieldTest.java
1 package Thread; 2 public class PriorityTest { 3 public static void main(String[] args){ 4 Thread t1=new MyThread_2(); 5 Thread t2=new Thread(new MyRunnable2()); 6 t2.start(); 7 t1.start(); 8 9 } 10 } 11 class MyThread_2 extends Thread{ 12 public void run(){ 13 for(int i=0;i<10;i++){ 14 System.out.println("執行緒1第"+i+"次執行!"); 15 /*try{ 16 Thread.sleep(7);//影響的是執行速度。 17 }catch(InterruptedException e){ 18 e.printStackTrace(); 19 }*/ 20 } 21 } 22 } 23 class MyRunnable2 implements Runnable{ 24 public void run(){ 25 for(int i=0;i<10;i++){ 26 System.out.println("執行緒2第"+i+"次執行!"); 27 Thread.yield(); 28 /*try{ 29 Thread.sleep(7); 30 }catch(InterruptedException e){ 31 e.printStackTrace(); 32 }*/ 33 } 34 } 35 }View Code
結果為:
1 執行緒1第0次執行! 2 執行緒1第1次執行! 3 執行緒1第2次執行! 4 執行緒1第3次執行! 5 執行緒1第4次執行! 6 執行緒1第5次執行! 7 執行緒1第6次執行! 8 執行緒1第7次執行! 9 執行緒1第8次執行! 10 執行緒1第9次執行! 11 執行緒2第0次執行! 12 執行緒2第1次執行! 13 執行緒2第2次執行! 14 執行緒2第3次執行! 15 執行緒2第4次執行! 16 執行緒2第5次執行! 17 執行緒2第6次執行! 18 執行緒2第7次執行! 19 執行緒2第8次執行! 20 執行緒2第9次執行!View Code
六、合併
合併就是將幾個並行執行緒的執行緒合併為一個單執行緒執行,應用場景就是當一個執行緒等待另一個執行緒執行完畢後才能執行,可以使用join方法。思想是當執行join方法後,主執行緒暫停,轉而執行新加入的執行緒,等到新加入的執行緒執行完畢後,才能繼續執行主執行緒。
1 void join() 2 等待該執行緒終止。 3 void join(long millis) 4 等待該執行緒終止的時間最長為millis 5 void join(long millis,int nanos) 6 等待該執行緒終止的最長時間為millis+nanos納秒
JoinTest.java
1 package Thread; 2 public class PriorityTest { 3 public static void main(String[] args){ 4 Thread t1=new MyThread_2(); 5 //Thread t2=new Thread(new MyRunnable2()); 6 //t2.start(); 7 t1.start(); 8 for(int i=0;i<20;i++){ 9 System.out.println("主執行緒第"+i+"次執行!"); 10 if(i>2) 11 try{ 12 t1.join();//t1執行緒合併到主執行緒中,主執行緒停止執行過程,轉而執行t1執行緒,直到t1執行完畢後繼續。 13 //Thread.sleep(7);//影響的是執行速度。 14 }catch(InterruptedException e){ 15 e.printStackTrace(); 16 } 17 } 18 } 19 } 20 class MyThread_2 extends Thread{ 21 public void run(){ 22 for(int i=0;i<10;i++){ 23 System.out.println("執行緒1第"+i+"次執行!"); 24 /*if(i>2) 25 try{ 26 Thread.sleep(7);//影響的是執行速度。 27 }catch(InterruptedException e){ 28 e.printStackTrace(); 29 }*/ 30 } 31 } 32 } 33 /*class MyRunnable2 implements Runnable{ 34 public void run(){ 35 for(int i=0;i<10;i++){ 36 System.out.println("執行緒2第"+i+"次執行!"); 37 Thread.yield(); 38 /*try{ 39 Thread.sleep(7); 40 }catch(InterruptedException e){ 41 e.printStackTrace(); 42 } 43 } 44 } 45 }*/View Code
結果為:
1 主執行緒第0次執行! 2 主執行緒第1次執行! 3 主執行緒第2次執行! 4 執行緒1第0次執行! 5 執行緒1第1次執行! 6 執行緒1第2次執行! 7 執行緒1第3次執行! 8 執行緒1第4次執行! 9 執行緒1第5次執行! 10 執行緒1第6次執行! 11 執行緒1第7次執行! 12 執行緒1第8次執行! 13 執行緒1第9次執行! 14 主執行緒第3次執行! 15 主執行緒第4次執行! 16 主執行緒第5次執行! 17 主執行緒第6次執行! 18 主執行緒第7次執行! 19 主執行緒第8次執行! 20 主執行緒第9次執行! 21 主執行緒第10次執行! 22 主執行緒第11次執行! 23 主執行緒第12次執行! 24 主執行緒第13次執行! 25 主執行緒第14次執行! 26 主執行緒第15次執行! 27 主執行緒第16次執行! 28 主執行緒第17次執行! 29 主執行緒第18次執行! 30 主執行緒第19次執行!View Code