本文承接上一篇文章《Java總結篇系列:Java多線程(一)》。java
四.Java多線程的阻塞狀態與線程控制多線程
上文已經提到Java阻塞的幾種具體類型。下面分別看下引發Java線程阻塞的主要方法。ide
1.join().net
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()code
sleep —— 讓當前的正在執行的線程暫停指定的時間,並進入阻塞狀態。在其睡眠的時間段內,該線程因爲不是處於就緒狀態,所以不會獲得執行的機會。即便此時系統中沒有任何其餘可執行的線程,出於sleep()中的線程也不會執行。所以sleep()方法經常使用來暫停線程執行。對象
前面有講到,當調用了新建的線程的start()方法後,線程進入到就緒狀態,可能會在接下來的某個時間獲取CPU時間片得以執行,若是但願這個新線程必然性的當即執行,直接調用原來線程的sleep(1)便可。blog
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); } } }