需求:用两个线程分别模拟 下载视频/播放视频 视频要在下载完成后才能播放。
实现方法:确定一个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。