线程的调度不是跨平台的,不仅取决于Java虚拟机,还依赖于操作系统。Java虚拟机采用抢占式调度,即优先让优先级高的线程先运行,如果优先级相同,则随机选择一个线程。Java线程的调度不是分时的,同时启动多个线程后,不能保证每个线程轮流得到相等的CPU时间片。
如果希望明确地让一个线程给其他线程运行的机会,有以下方法:
- 调整各个线程的优先级
- 处于运行态的线程调用Thread.sleep()方法
- 处于运行态的线程调用Thread.yield()方法
- 处于运行态的线程调用另一个线程的join()方法
调整各个线程的优先级
优先级高的线程会获得优先运行的机会。Thread类的setPriority(int)和getPriority()方法分别用来设置优先级和获得优先级。优先级是整数,取值范围是1 ~ 10,Thread有3个静态常量:
- MAX_PRIORITY:值为10,最高优先级
- MIN_PROIRITY:值为1,最低优先级
- NORM_PRIORITY:值为5,默认优先级
每个线程都有默认优先级,如果线程A创建了线程B,那么它们具有相同的优先级。
线程睡眠:Thread.sleep()方法
当一个线程在运行态执行了sleep()方法,那么它会放弃cpu,转到阻塞态,结束睡眠时,转到就绪态。
如果线程在睡眠时被中断,就会收到一个InterrupedException异常,线程跳到异常处理代码块。
public class Main extends Thread{
public void run() {
try {
sleep(1000);
System.out.println("Sleep over");
} catch (InterruptedException e) {
System.out.println("Interruped!");
}
System.out.println("end");
}
public static void main(String args[]) throws Exception {
Main m = new Main();
m.start();
//Thread.sleep(2000);
m.interrupt();
}
}
运行结果:
Interruped!
end
主线程启动Main线程,Main线程睡眠1秒,此时Main线程被中断,所以会转到异常处理代码块。
public class Main extends Thread{
public void run() {
try {
sleep(1000);
System.out.println("Sleep over");
} catch (InterruptedException e) {
System.out.println("Interruped!");
}
System.out.println("end");
}
public static void main(String args[]) throws Exception {
Main m = new Main();
m.start();
Thread.sleep(2000);
m.interrupt();
}
}
运行结果:
Sleep over
end
主线程启动Main线程,Main线程睡眠1秒,此时主线程睡眠2秒后中断Main线程的睡眠,而2秒后Main线程已经是运行态,所以中断无效,不会触发异常处理。
线程让步:Thread.yield()方法
当线程执行了yield()静态方法时,如果此时有相同或更高优先级的线程处于就绪态,那么,当前处于运行态的线程会被放到可运行池中,转到就绪态,另外一个符合要求的线程运行。如果没有符合的线程,yield()方法什么也不做。
等待其他线程结束:join()方法
当运行态的线程调用另外一个线程的join()方法,当前线程将转为阻塞态,直到另外一个线程运行结束,它才会恢复运行(阻塞态->就绪态)。
public class Main extends Thread{
public void run() {
for(int i=0;i<5;i++) {
System.out.println(getName()+" : "+i);
}
}
public static void main(String args[]) throws Exception {
Main m1 = new Main();
m1.setName("m1");
m1.start();
System.out.println("main:join m1");
m1.join();//主线程等待m1线程结束
System.out.println("main:end");
}
}
运行结果:
main:join m1
m1 : 0
m1 : 1
m1 : 2
m1 : 3
m1 : 4
main:end
join()有2种重载形式:
public void join()
public void join(long timeout)
timeout参数设定当前线程被阻塞的时间,单位:毫秒。如果把上面改为m1.join(1000),那么当主线程被阻塞的时间超过了1秒时,或者m1线程运行结束时,主线程恢复运行。