本文承接上一篇文章《Java多線程執行個體詳解(一)》。
四.Java多線程的阻塞狀態與線程式控制制
上文已經提到Java阻塞的幾種具體類型。下面分別看下引起Java線程阻塞的主要方法。
1.join()
join —— 讓一個線程等待另一個線程完成才繼續執行。如A線程線程執行體中調用B線程的join()方法,則A線程被阻塞,知道B線程執行完為止,A才能得以繼續執行。
public class ThreadTest { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { thread.start(); try { thread.join(); // main線程需要等待thread線程執行完後才能繼續執行 } catch (InterruptedException e) { e.printStackTrace(); } } } }}class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }}
2.sleep()
sleep —— 讓當前的正在執行的線程暫停指定的時間,並進入阻塞狀態。在其睡眠的時間段內,該線程由於不是處於就緒狀態,因此不會得到執行的機會。即使此時系統中沒有任何其他可執行檔線程,出於sleep()中的線程也不會執行。因此sleep()方法常用來暫停線程執行。
前面有講到,當調用了建立的線程的start()方法後,線程進入到就緒狀態,可能會在接下來的某個時間擷取CPU時間片得以執行,如果希望這個新線程必然性的立即執行,直接調用原來線程的sleep(1)即可。
public class ThreadTest { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 30) { thread.start(); try { Thread.sleep(1); // 使得thread必然能夠馬上得以執行 } catch (InterruptedException e) { e.printStackTrace(); } } } }}class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } }}
註:睡一個毫秒級夠了,因為CPU不會空閑,會切換到建立的線程。
3.後台線程(Daemon Thread)
概念/目的:後台線程主要是為其他線程(相對可以稱之為前台線程)提供服務,或“守護線程”。如JVM中的記憶體回收線程。
生命週期:後台線程的生命週期與前台線程生命週期有一定關聯。主要體現在:當所有的前台線程都進入死亡狀態時,後台線程會自動死亡(其實這個也很好理解,因為後台線程存在的目的在於為前台線程服務的,既然所有的前台線程都死亡了,那它自己還留著有什麼用...偉大啊 ! !)。
設定後台線程:調用Thread對象的setDaemon(true)方法可以將指定的線程設定為後台線程。
public class ThreadTest { public static void main(String[] args) { Thread myThread = new MyThread(); for (int i = 0; i < 100; i++) { System.out.println("main thread i = " + i); if (i == 20) { myThread.setDaemon(true); myThread.start(); } } }}class MyThread extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("i = " + i); try { Thread.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}
判斷線程是否是後台線程:調用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()。
註:具有較高線程優先順序的線程對象僅表示此線程具有較多的執行機會,而非優先執行。
public class ThreadTest { public static void main(String[] args) { Thread myThread = new MyThread(); for (int i = 0; i < 100; i++) { System.out.println("main thread i = " + i); if (i == 20) { myThread.setPriority(Thread.MAX_PRIORITY); myThread.start(); } } }}class MyThread extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("i = " + i); } }}
5.線程讓步:yield()
上一篇博文中已經講到了yield()的基本作用,同時,yield()方法還與線程優先順序有關,當某個線程調用yiled()方法從運行狀態轉換到就緒狀態後,CPU從就緒狀態線程隊列中只會選擇與該線程優先順序相同或優先順序更高的線程去執行。
public class ThreadTest { public static void main(String[] args) { Thread myThread1 = new MyThread1(); Thread myThread2 = new MyThread2(); myThread1.setPriority(Thread.MAX_PRIORITY); myThread2.setPriority(Thread.MIN_PRIORITY); for (int i = 0; i < 100; i++) { System.out.println("main thread i = " + i); if (i == 20) { myThread1.start(); myThread2.start(); Thread.yield(); } } }}class MyThread1 extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("myThread 1 -- i = " + i); } }}class MyThread2 extends Thread { public void run() { for (int i = 0; i < 100; i++) { System.out.println("myThread 2 -- i = " + i); } }}
系列文章:
java 多線程執行個體講解 (一)
Java 多線程執行個體詳解(二)
Java 多線程執行個體詳解(三)