Java多線程的阻塞狀態與控制
上文已經提到Java阻塞的幾種具體類型。下面分別看下引起Java線程阻塞的主要方法。
等待加入:join()
join —— 讓一個線程等待另一個線程完成才繼續執行。如A線程線程執行體中調用B線程的join()方法,則A線程被阻塞,知道B線程執行完爲止,A才能得以繼續執行。
public class MyRunnable implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
try {
//main線程執行到此時,需等待t1線程完成才能繼續執行
t1.join();
System.out.println(Thread.currentThread().getName() + " end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
線程睡眠:Thread.sleep()
sleep —— 讓當前的正在執行的線程暫停指定的時間,並進入阻塞狀態。在其睡眠的時間段內,該線程由於不是處於就緒狀態,因此不會得到執行的機會。即使此時系統中沒有任何其他可執行的線程,處於sleep()中的線程也不會執行。因此sleep()方法常用來暫停線程執行。
public class MyRunnable implements Runnable {
@Override
public void run() {
try {
//可以調用Thread的靜態方法sleep或者時間顯示更加直觀的TimeUnit
//Thread.sleep(5000);
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
後臺線程(Daemon Thread)
概念/目的:後臺線程主要是爲其他線程(相對可以稱之爲前臺線程)提供服務,或“守護線程”。如JVM中的垃圾回收線程。
生命週期:後臺線程的生命週期與前臺線程生命週期有一定關聯。主要體現在:當所有的前臺線程都進入死亡狀態時,後臺線程會自動死亡(其實這個也很好理解,因爲後臺線程存在的目的在於爲前臺線程服務的,既然所有的前臺線程都死亡了,那它自己留着也就沒有什麼用了)。
設置後臺線程:調用Thread對象的setDaemon(true)方法可以將指定的線程設置爲後臺線程。
public class MyRunnable implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end");
}
//main線程結束後,因爲t1爲後臺線程,會直接通知結束,不會等待5s線程執行完成
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.setDaemon(true);
t1.start();
System.out.println(Thread.currentThread().getName() + " end");
}
}
判斷線程是否是後臺線程:調用thread對象的isDeamon()方法。
注:main線程默認是前臺線程,前臺線程創建中創建的子線程默認是前臺線程,後臺線程中創建的線程默認是後臺線程。調用setDeamon(true)方法將前臺線程設置爲後臺線程時,需要在start()方法調用之前。前臺線程都死亡後,JVM通知後臺線程死亡,但從接收指令到作出響應,需要一定的時間。
改變線程的優先級:setPriority()
每個線程在執行時都具有一定的優先級,優先級高的線程具有較多的執行機會。每個線程默認的優先級都與創建它的線程的優先級相同。main線程默認具有普通優先級。
設置線程優先級:setPriority(int priorityLevel)。參數priorityLevel範圍在1-10之間,常用的有如下三個靜態常量值:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
獲取線程優先級:getPriority()。
注:具有較高線程優先級的線程對象僅表示此線程具有較多的執行機會,而非優先執行。
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " end");
}
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "Thread1");
t1.setPriority(1);
Thread t2 = new Thread(runnable, "Thread2");
t2.setPriority(2);
Thread t3 = new Thread(runnable, "Thread3");
t3.setPriority(3);
Thread t4 = new Thread(runnable, "Thread4");
t4.setPriority(4);
Thread t5 = new Thread(runnable, "Thread5");
t5.setPriority(5);
Thread t6 = new Thread(runnable, "Thread6");
t6.setPriority(10);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
System.out.println(Thread.currentThread().getName() + " end");
}
}
main end
Thread6 end
Thread5 end
Thread3 end
Thread4 end
Thread1 end
Thread2 end
線程讓步:Thread.yield()
上一篇博文中已經講到了yield()的基本作用,同時,yield()方法還與線程優先級有關,當某個線程調用yiled()方法從運行狀態轉換到就緒狀態後,CPU從就緒狀態線程隊列中只會選擇與該線程優先級相同或優先級更高的線程去執行。
public class MyRunnable implements Runnable {
@Override
public void run() {
Thread.yield();
System.out.println(Thread.currentThread().getName() + " end");
}
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "Thread1");
t1.setPriority(1);
Thread t2 = new Thread(runnable, "Thread2");
t2.setPriority(5);
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName() + " end");
}
}
main end
Thread2 end
Thread1 end