需求:用兩個線程分別模擬 下載視頻/播放視頻 視頻要在下載完成後才能播放。
實現方法:確定一個boolean變量,只有在下載完成後才變成true,在播放視頻的線程中不斷判斷這個條件已達到下載完成後才播放的目的。這種方法可行但會耗費系統資源(不斷判斷的過程)。
更好的實現方法:使用join方法,在一個線程內調用另一個線程實例的join()方法,表示線程在此阻塞,以等待相應的實例執行完成。
代碼:
public class DownloadDemo {
private static boolean isFinish = false;
public static void main(String args[]){
Thread download = new Thread(){
public void run(){
for(int i=0;i<100;i++){
System.out.println("已經加載:"+i+"%");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isFinish = true;
System.out.println("加載完畢。");
}
};
Thread play = new Thread(){
public void run(){
try {
download.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
while(!isFinish) //執行過程中,控制檯如果不輸出此條件下的語句
//則表示在上方阻塞,一直到需要的線程執行完畢
System.out.println("加載未完成!");
System.out.println("正在播放!");
}
};
download.start();
play.start();
}
}
執行過程中,控制檯沒有輸出“加載未完成!”,表示程序達到了預期的效果。
需求2:加載完視頻後,download線程還要加載流氓插件,但是加載完視頻以後就可以播放了。
分析:上面的方法已經不能實現此需求,因爲一旦download.join(),就表示一定要等待download線程執行完,也就是流氓插件加載完後,才解除阻塞。
實現方法:使用wait()/notify()方法。
調用此方法的實例是一個除去需要被規劃的線程實例之外的第三方實例(可以將其看作通訊人)。
例如:Object obj = new Object(); 然後在play線程中調用obj.wait(),再在download線程中調用obj.notify()或obj.notifyAll() (區別,喚醒此實例控制的隨機線程/喚醒全部線程)。
另外,API文檔上說明:調用某個對象的wait或notify方法時,該方法需要使用synchronized代碼塊進行同步(這裏不明白爲什麼,求大神解釋)而鎖對象就是wait()與notify()的所屬對象,否則在實際運行過程中會發生異常。
代碼:
public class DownloadDemo {
private static boolean isFinish = false;
public static void main(String args[]){
Thread download = new Thread(){
public void run(){
for(int i=0;i<100;i++){
System.out.println("已經加載:"+i+"%");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isFinish = true;
System.out.println("加載完畢。");
synchronized(DownloadDemo.class){
DownloadDemo.class.notify(); //使用字節碼文件當鎖實現wait/notify (絕對不是因爲我懶得新建一個Object)
}
for(int i=0;i<100;i++){
System.out.println("已經加載:"+i+"%");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("插件加載完畢。");
}
};
Thread play = new Thread(){
public void run(){
try {
synchronized(DownloadDemo.class){
DownloadDemo.class.wait(); //調用等待。
}
} catch (InterruptedException e) {
e.printStackTrace();
}
while(!isFinish)
System.out.println("加載未完成!");
System.out.println("正在播放!");
}
};
download.start();
play.start();
}
}
wait與sleep的區別——wait是讓出資源,施放鎖。sleep是霸佔着資源,還霸佔着鎖。
備忘:明天寫synchronized API和線程池,看Thinking in Java。