執行緒與多執行緒(四)——執行緒排程
四、執行緒排程
執行緒排程管理器負責執行緒排隊和CPU線上程間的分配,並按執行緒排程演算法進行排程。當執行緒排程管理器選中某個執行緒時,該執行緒獲得 CPU資源進人執行狀態。
執行緒排程是搶佔式排程,即如果在當前執行緒執行過程中個更高優先順序的執行緒進人可執行狀態,則這個更高優先順序的執行緒立即被排程執行。
3.1實現執行緒排程的方法
1. join( )方法
join( )方法使當前執行緒暫停執行,等待呼叫該方法的執行緒結束後再繼續執行本執行緒。它被過載了3次:
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
下面通過示例具體看一下join( )方法的應用。
示例1
使用join( )方法阻塞執行緒。
實現步驟:
(1)定義執行緒類,輸出5次當前執行緒的名稱。
(2)定義測試類,使用join()方法阻塞主執行緒。
// 執行緒類
public class MyThread extends Thread(
public MyThread(string name){
super(name);
}
public void run(){
for (int i=0;i<5;i++){
//輸出當前執行緒的名稱
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
// 測試類
public class Test{
/*
* Java 程式中的public static void main( )方法是主執行緒的入口,
* 執行Java程式時,會先執行這個方法。
*/
public static void main(String[] args) {
for (int i = 0 ; i < 10; i++) {
if (i == 5) {
System.out.println("i:" + i);
// 主執行緒執行5次後,開始myThread的執行緒
MyThread myThread = new MyThread("myThread");
try {
myThread.start();
// join()方法使當前執行緒暫停執行,等待呼叫該方法的執行緒結束後再繼續執行本執行緒
// 把該執行緒通過join()方法插入到主執行緒前面,否則主執行緒執行完畢後再執行該執行緒
myThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// main,主執行緒
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
}
// 再解釋一遍流程。
// 1、主執行緒開始執行。
// 2、i等於5的時候,主執行緒暫停。
// 3、等待呼叫該方法的執行緒myThread執行完畢後繼續執行主執行緒main。
main-0
main-1
main-2
main-3
main-4
i:5
myThread-0
myThread-1
myThread-2
myThread-3
myThread-4
main-5
main-6
main-7
main-8
main-9
在示例1中,使用join( )方法阻塞指定的執行緒等到另一個執行緒完成以後再繼續執行。其中myThread.join( )表示讓當前執行緒即主執行緒main加到myThread的末尾,主執行緒被阻塞,myThread執行完以後主執行緒才能繼續執行。
Thread.curentThread().geName( )獲取了當前執行緒的名稱。
從執行緒返回資料時也經常使用到join()方法。
示例2
使用join( )方法實現兩個執行緒間的資料傳遞。
實現步驟:
(1)定義執行緒類,為變數賦值。
(2)定義測試類。
public class MyThread extends Thread{
public String valuel;
public String value2;
public void run() {
valuel = "value1已賦值";
value2 = "value2已賦值";
}
}
public class Test {
public static void main(Stringl[] args) throws InterruptedException{
MyThread thread = new MyThread();
thread.start();
System.out.println("value1 :"+thread.value1);
System.out.printin("value2 :"+thread.value2);
}
}
// 輸出結果:
valuel: null
value2: null
在示例2中,在run( )方法中已經對value1和value2賦值,而返回的卻是null,發生這種情況的原因是在主執行緒中呼叫start( )方法後就立刻輸出了value1和value2的值,而這裡run( )方法可能還沒有執行到為valuel 和value2賦值的語句。要避免這種情況的發生,需要等run( )方法執行完後才執行輸出value1和value2的程式碼,可以使用join( )方法來解決這個問題。修改示例2的程式碼,在“thread.start( );”後新增”thread.join();”,修改後此處程式碼如下:
public class Test {
public static void main(Stringl[] args) throws InterruptedException{
MyThread thread = new MyThread();
thread.start();
// join()方法
thread.join();
System.out.println("value1 :"+thread.value1);
System.out.printin("value2 :"+thread.value2);
}
}
// 重新執行示例2,則可以得到如下輸出結果:
valuel: value1 己賦值
value2: value2 已賦值
2.sleep( )方法
sleep( )方法定義語法如下:
public static void sleep(long millis)
sleep( )方法會讓當 前執行緒睡眠(停止執行) millis毫秒,執行緒由執行中的狀態進人不可執行狀態,
睡眠時間過後執行緒會再進人可執行狀態。
示例1
使用sleep( )方法阻塞執行緒。
實現步驟:
(1)定義執行緒。
(2)在run( )方法中使用sleep( )方法阻塞執行緒。
(3)定義測試類。
public class SleepThreadTest {
public static void bySec(long s) {
for (int i = 0; i < s; i++) {
try {
// 睡眠1000毫秒
Thread.sleep(1000);
System.out.println("執行緒---"+Thread.currentThread().getName()+" 等待---"+ (i + 1) + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println("please wait...");
// 讓主(該)執行緒等等5秒再執行
SleepThreadTest.bySec(5);
System.out.println("start...");
}
}
// 執行結果
please wait...
執行緒---main 等待---1秒
執行緒---main 等待---2秒
執行緒---main 等待---3秒
執行緒---main 等待---4秒
執行緒---main 等待---5秒
start...
示例3的程式碼中,在執行主執行緒以後,首先輸出了please wait,然後主執行緒等待5秒後繼續執行。
3.yield( )方法
yield( )方法定義語法如下:
public static void yield()
yield( )方法可暫停當前執行緒執行,允許其他執行緒執行, 該執行緒仍處於可執行狀態,不轉為阻塞狀態。此時,系統選擇其他相同或更高優先順序執行緒執行,若無其他相同或更高優先順序執行緒,則該執行緒繼續執行。
示例1
使用yield()方法暫停執行緒。
實現步驟:
(1)定義兩個執行緒。
(2)在run( )方法中使用yield( )方法暫停執行緒。
(3)定義測試類。
// 執行緒類1
public class FirstThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("第一個執行緒的第" + (i + 1) + "次執行" + Thread.currentThread().getName());
// 暫停執行緒
Thread.yield();
}
}
}
// 執行緒類2
public class SecThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("第二個執行緒的第" + (i + 1) + "次執行" + Thread.currentThread().getName());
// 暫停執行緒
Thread.yield();
}
}
}
// 測試類
public class Test {
public static void main(String[] args) {
FirstThread t1 = new FirstThread();
SecThread t2 = new SecThread();
t1.start();
t2.start();
}
}
// 執行結果,兩個執行緒類都用了yield()方法,它們都還是可執行狀態,如果沒有相同更高優先順序執行緒,它們互相交替執行
第一個執行緒的第1次執行Thread-0
第一個執行緒的第2次執行Thread-0
第一個執行緒的第3次執行Thread-0
第一個執行緒的第4次執行Thread-0
第二個執行緒的第1次執行Thread-1
第二個執行緒的第2次執行Thread-1
第二個執行緒的第3次執行Thread-1
第二個執行緒的第4次執行Thread-1
第二個執行緒的第5次執行Thread-1
第一個執行緒的第5次執行Thread-0 // 又是第一個執行緒
在示例6中,呼叫了yield( )方法之後,當前執行緒並不是轉人被阻塞狀態,它可以與其他等待執行的執行緒競爭CPU資源,如果此時它又搶佔到CPU資源,就會出現連續執行幾次的情況。sleep( )方法與yield( )方法在使用時容易混淆,這兩個方法之間的區別如下表所示。
sleep( )方法 | yield( )方法 |
---|---|
使當前執行緒進入被阻塞狀態 | 將當前執行緒轉入暫停執行狀態 |
即使沒有其他等待執行的執行緒,當前執行緒也會等待指定的時間 | 如果沒有其他等待的執行緒,當前執行緒會馬上恢復執行 |
其他等待執行的執行緒的機會是均等的 | 會將優先順序相同或更高的執行緒執行 |