Thread->sleep、wait、join使用
Thread sleep、wait、join使用
這裡先介紹join,然後把兩個有關聯的sleep和wait一起介紹.
join()
這個方法比較好理解,當前執行緒等待指定執行緒終止後在執行,將兩個交替執行的執行緒合併為順序執行的執行緒.比如在B執行緒中呼叫A執行緒的join()方法,直到A執行緒執行完畢,B執行緒才會繼續執行.
api有兩個
void join()
當前執行緒等待呼叫這個方法的執行緒終止後再執行.
void join(long millis)
當前執行緒等待millis毫秒後,不管呼叫這個方法的執行緒是否終止,當前執行緒將變成可執行狀態
這裡我們不妨看一下join的原始碼,join()中呼叫的join(long millis)所以這裡我只貼出join(long millis)方法
可以發現他先呼叫isAlive()方法判斷該執行緒是否存活,然後通過Object.wait(0)方法使當前執行緒阻塞(客官別急wait後面會講到).
通過註釋可以發現執行緒存活指的是執行緒存活並且呼叫start()了.接下來我們通過一個例子體會下join的使用
public class MyClass {
public static void main(String[] args) {
System.out.println("main start");
Thread t = new Thread(new MyRunnable());
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main end");
}
private static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("other start" );
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("other end");
}
}
}
執行結果.
先列印了main start,然後由於我們呼叫了t.join()於是主執行緒等待other執行緒執行完畢後在繼續執行,所以打印出other start,other end,最後打印出main end.
接下來我們將join()方法替換成join(1000)
執行結果.
這裡應該沒啥問題就不多說了.
sleep()
Thread靜態方法,強制當前執行緒休眠.當休眠時間到期後恢復到可執行狀態.不一定會立即執行,具體取決於執行緒排程器.
下面一個很簡單的栗子
public static void main(String[] args) {
System.out.println("main start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main end");
}
結果就是先列印main start,然後休眠3秒後列印,main end.
我主要想說的是下面這個問題,sleep()在Synchronized塊中呼叫,執行緒雖然休眠了,但是物件的鎖並未釋放,其他執行緒無法訪問這個物件.下面一個例子展示這個過程.
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
r.method1();
}
private static class MyRunnable implements Runnable {
@Override
public synchronized void run() {
System.out.println("other start");
System.out.println("other end");
}
public synchronized void method1(){
System.out.println("main start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main end");
}
}
結果如下
可以發現即使method1()中呼叫了Thread.sleep(),但物件鎖並未釋放,所以休眠3秒後依次執行main end->other start->other end.這裡可以留意下後面在wait()方法的時候我們會做個對比.
wait()
wait()一般都是配合notify()和notifyAll()一起使用,並且他們都是java.lang.Object的方法.
具體細節如下
wait()方法是使當前執行緒,進入到一個和物件相關的等待池中,同時失去物件的鎖,其他執行緒可以訪問.
wait()方法通過notify(),notifyAll(),或者指定超時時間來喚醒當前等待池中執行緒.
wait()必須用在synchronized程式碼塊中,否則會丟擲異常.
下面通過一個栗子檢驗下
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
r.method1();
}
private static class MyRunnable implements Runnable {
@Override
public synchronized void run() {
System.out.println("other start");
System.out.println("other end");
notifyAll();
}
public synchronized void method1() {
System.out.println("main start");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main end");
}
}
執行結果如下
由於執行緒建立需要時間,所以先執行了method1()方法打印出main start,然後呼叫wait()方法後該執行緒進入等待狀態,並且釋放該物件鎖,於是run()執行打印出other start和other end,然後呼叫notifyAll()後wait的執行緒被喚醒打印出main end.
這裡細說下notify()與notifyAll(),
notify()
舉個例子如果執行緒A1,A2,A3都obj.wait(),則B呼叫obj.notify()只能喚醒A1,A2,A3中的一個(具體哪一個由JVM決定)
notifyAll()
obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續執行obj.wait()的下一條語句,必須獲得obj鎖,因此,A1,A2,A3只有一個有機會獲得鎖繼續執行,例如A1,其餘的需要等待A1釋放obj鎖之後才能繼續執行.
sleep()與wait()區別
sleep()
- sleep是Thread靜態方法,因此他不能改變物件的機鎖,所以當在一個Synchronized塊中呼叫Sleep()方法是,執行緒雖然休眠了,但是物件的鎖並木有被釋放,其他執行緒無法訪問這個物件(即使睡著也持有物件鎖).
wait()
wait()是Object類裡的方法,當一個執行緒執行到wait()方法時,它就進入到一個和該物件相關的等待池中,同時失去了物件的鎖,其他執行緒可以訪問.
wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的執行緒
wait()必須放在synchronized block中,否則會在執行時丟擲”java.lang.IllegalMonitorStateException”異常.
一句話總結最大區別:sleep()睡眠時,保持物件鎖,仍然佔有該鎖;而wait()睡眠時,釋放物件鎖.
總結
join將並行執行的執行緒改為序列.
sleep使當前執行緒休眠,目的是不讓當前執行緒獨自霸佔該程序所獲的CPU資源,以留一定時間給其他執行緒執行的機會.
在某種條件下該執行緒wait(),當條件成熟後通過notify()喚醒該執行緒繼續執行.