本文承接上一篇文章《Java總結篇系列:Java多線程(一)》。
四.Java多線程的阻塞狀態與線程控制
上文已經提到Java阻塞的幾種具體類型。下面分別看下引起Java線程阻塞的主要方法。
1.join()
join —— 讓一個線程等待另一個線程完成才繼續執行。如A線程線程執行體中調用B線程的join()方法,則A線程被阻塞,知道B線程執行完為止,A才能得以繼續執行。
1 public class ThreadTest { 2
3 public static void main(String[] args) { 4
5 MyRunnable myRunnable = new MyRunnable(); 6 Thread thread = new Thread(myRunnable); 7
8 for (int i = 0; i < 100; i++) { 9 System.out.println(Thread.currentThread().getName() + " " + i); 10 if (i == 30) { 11 thread.start(); 12 try { 13 thread.join(); // main線程需要等待thread線程執行完后才能繼續執行
14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 } 18 } 19 } 20 } 21
22 class MyRunnable implements Runnable { 23
24 @Override 25 public void run() { 26 for (int i = 0; i < 100; i++) { 27 System.out.println(Thread.currentThread().getName() + " " + i); 28 } 29 } 30 }
2.sleep()
sleep —— 讓當前的正在執行的線程暫停指定的時間,並進入阻塞狀態。在其睡眠的時間段內,該線程由於不是處於就緒狀態,因此不會得到執行的機會。即使此時系統中沒有任何其他可執行的線程,出於sleep()中的線程也不會執行。因此sleep()方法常用來暫停線程執行。
前面有講到,當調用了新建的線程的start()方法后,線程進入到就緒狀態,可能會在接下來的某個時間獲取CPU時間片得以執行,如果希望這個新線程必然性的立即執行,直接調用原來線程的sleep(1)即可。
1 public class ThreadTest { 2
3 public static void main(String[] args) { 4
5 MyRunnable myRunnable = new MyRunnable(); 6 Thread thread = new Thread(myRunnable); 7
8 for (int i = 0; i < 100; i++) { 9 System.out.println(Thread.currentThread().getName() + " " + i); 10 if (i == 30) { 11 thread.start(); 12 try { 13 Thread.sleep(1); // 使得thread必然能夠馬上得以執行
14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 } 18 } 19 } 20 } 21
22 class MyRunnable implements Runnable { 23
24 @Override 25 public void run() { 26 for (int i = 0; i < 100; i++) { 27 System.out.println(Thread.currentThread().getName() + " " + i); 28 } 29 } 30 }
注:睡一個毫秒級夠了,因為CPU不會空閑,會切換到新建的線程。
3.后台線程(Daemon Thread)
概念/目的:后台線程主要是為其他線程(相對可以稱之為前台線程)提供服務,或“守護線程”。如JVM中的垃圾回收線程。
生命周期:后台線程的生命周期與前台線程生命周期有一定關聯。主要體現在:當所有的前台線程都進入死亡狀態時,后台線程會自動死亡(其實這個也很好理解,因為后台線程存在的目的在於為前台線程服務的,既然所有的前台線程都死亡了,那它自己還留着有什么用...偉大啊 ! !)。
設置后台線程:調用Thread對象的setDaemon(true)方法可以將指定的線程設置為后台線程。
1 public class ThreadTest { 2
3 public static void main(String[] args) { 4 Thread myThread = new MyThread(); 5 for (int i = 0; i < 100; i++) { 6 System.out.println("main thread i = " + i); 7 if (i == 20) { 8 myThread.setDaemon(true); 9 myThread.start(); 10 } 11 } 12 } 13
14 } 15
16 class MyThread extends Thread { 17
18 public void run() { 19 for (int i = 0; i < 100; i++) { 20 System.out.println("i = " + i); 21 try { 22 Thread.sleep(1); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block
25 e.printStackTrace(); 26 } 27 } 28 } 29 }
判斷線程是否是后台線程:調用thread對象的isDeamon()方法。
注:main線程默認是前台線程,前台線程創建中創建的子線程默認是前台線程,后台線程中創建的線程默認是后台線程。調用setDeamon(true)方法將前台線程設置為后台線程時,需要在start()方法調用之前。前天線程都死亡后,JVM通知后台線程死亡,但從接收指令到作出響應,需要一定的時間。
4.改變線程的優先級/setPriority():
每個線程在執行時都具有一定的優先級,優先級高的線程具有較多的執行機會。每個線程默認的優先級都與創建它的線程的優先級相同。main線程默認具有普通優先級。
設置線程優先級:setPriority(int priorityLevel)。參數priorityLevel范圍在1-10之間,常用的有如下三個靜態常量值:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
獲取線程優先級:getPriority()。
注:具有較高線程優先級的線程對象僅表示此線程具有較多的執行機會,而非優先執行。
1 public class ThreadTest { 2
3 public static void main(String[] args) { 4 Thread myThread = new MyThread(); 5 for (int i = 0; i < 100; i++) { 6 System.out.println("main thread i = " + i); 7 if (i == 20) { 8 myThread.setPriority(Thread.MAX_PRIORITY); 9 myThread.start(); 10 } 11 } 12 } 13
14 } 15
16 class MyThread extends Thread { 17
18 public void run() { 19 for (int i = 0; i < 100; i++) { 20 System.out.println("i = " + i); 21 } 22 } 23 }
5.線程讓步:yield()
上一篇博文中已經講到了yield()的基本作用,同時,yield()方法還與線程優先級有關,當某個線程調用yiled()方法從運行狀態轉換到就緒狀態后,CPU從就緒狀態線程隊列中只會選擇與該線程優先級相同或優先級更高的線程去執行。
1 public class ThreadTest { 2
3 public static void main(String[] args) { 4 Thread myThread1 = new MyThread1(); 5 Thread myThread2 = new MyThread2(); 6 myThread1.setPriority(Thread.MAX_PRIORITY); 7 myThread2.setPriority(Thread.MIN_PRIORITY); 8 for (int i = 0; i < 100; i++) { 9 System.out.println("main thread i = " + i); 10 if (i == 20) { 11 myThread1.start(); 12 myThread2.start(); 13 Thread.yield(); 14 } 15 } 16 } 17
18 } 19
20 class MyThread1 extends Thread { 21
22 public void run() { 23 for (int i = 0; i < 100; i++) { 24 System.out.println("myThread 1 -- i = " + i); 25 } 26 } 27 } 28
29 class MyThread2 extends Thread { 30
31 public void run() { 32 for (int i = 0; i < 100; i++) { 33 System.out.println("myThread 2 -- i = " + i); 34 } 35 } 36 }