线程调度
前言
计算机通常只有一个CPU。所谓多线程的并发运行,其实是指宏观上看各个线程轮流获得CPU的使用权。Java虚拟机就是负责线程的调度
一、 两种调度模型
1.1 分时调度模型
指让所有线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU的时间。
1.2 抢占调度(JAVA虚拟机采用的调度模式)
指优先让可以运行池中优先级高的线程占用CPU,如果可运行线程池中的优先级相同,那么就随机地选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。
1.3 、线程放弃CPU的原因
- Java虚拟机让当前线程暂时放弃CPU ,转到就绪状态,使其他线程获得运行机会。
- 当前线程因为某些原因进入阻塞状态
- 线程运行结束
Tips
1.线程的调度不是跨平台的,它不仅仅取决于Java寻虚拟机,还依赖于操作系统。
2.Java线程的调度不是分时的,同时启动多个线程后不能保证各个线程轮流获得均等的CPU时间片
二 、Java明确让一个线程给另外一个线程运行机会采用的方法
- 调整各个线程的优先级
- 让处于运行的线程调用Thread.sleep()方法
- 让处于运行状态的线程调用Thread.yueld()方法
- 让处于运行状态的线程调用另一个线程的join()方法
2.1 调整各个线程的优先级
所有处于就绪状态的线程根据优先级存放在可运行池中,优先级低的线程获得较少的运行机会。
Thread类的**setPriority(int i)和 getPriority()**方法分别来设置优先级和读取优先级。取值范围1~10,Thread常用三个静态变量(建议调整线程优先级时适用默认值)
- MAX_PRIORITY: 取值为10,表示最高优先级
- MIN_PRIORITY:取值为1,表示最低优先级
- NORM_PRIORITY:取值为5,表示默认优先级
注意:
- 每个线程都有默认的优先级,主线程的默认优先级为Thread.NORM_PRIORITY。如果线程A创建了线程B,那么B将和A具有同样的优先级。
- 尽管java提供了10个优先级,但是它与多数操作系统都不能很好的进行线程优先级的映射。比如Window有7个优先级,并不是固定的。而Solaris操作系统有2^31个优先级。如果希望程序能够移植到各个操作系统中,因该确保在设置线程的优先级时,只使用MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY这三个默认优先级。
2.2、线程睡眠: Thread.sleep()方法
当一个线程执行了sleep()方法,就会放弃CPU,转到阻塞状态。sleep(Long millis)参数设定睡眠时间,单位:毫秒。当线程睡眠结束,首先转到就绪状态
public class Sleeper extends Thread {
@Override
public void run() {
try {
sleep(60000);
System.out.println("sleep over");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end");
}
public static void main(String[] args) throws InterruptedException {
Sleeper sleeper = new Sleeper();
sleeper.start();
Thread.sleep(10);
sleeper.interrupt();// 终端睡眠
}
}
2.3、线程让步:Thread.yield()方法
当前线程执行yield()静态方法时,如果此时具有相同优先级的其他线程处于就绪状态,yield()方法将当前运行的线程放到可运行池中并使另一个线程运行。如果没有相同优先级的可运行进程,yield()方法什么都不做。
2.4、sleep()和yield()区别
- sleep()方法会给其他线程运行的机会,不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行机会。
- 当线程执行了sleep(Long millis)方法后,将转到
阻塞状态
,参数millis指定睡眠时间,当线程执行额yield()方法以后,将转到就绪状态
。 - sleep()方法生命抛出InterruptddException异常,yield()方法不抛出异常
- sleep()方法比yield()方法具有更好的可移植性。不能依靠yield()方法来提高程序的并发性能。实际开发中几乎不适用yield()方法提高并发性能.
2.5、等待其他线程结束:join()
当前运行的线程可以调用另一个线程的join()方法。当前运行的线程转到阻塞状态,直至另一个线程运行结束,他才回复运行
2.5.1、 join()方法的两种重载形式
public void join()
public void join(Long timeOut)
timeout参数设定当前线程被阻塞的时间,单位:毫秒
machine.join(10)
当主线程被阻塞的时间超过10毫秒,或者machine线程运行结束时,主线程就恢复运行
public class Machine extends Thread {
@Override
public void run() {
for (int a = 0; a < 200; a++) {
System.out.println(getName() + ":" + a);
}
}
public static void main(String[] args) throws InterruptedException {
Machine machine = new Machine ();
machine .setName("t1");
machine .start();
System.out.println("main:join machine");
machine .join(10);
System.out.println("main:end");// 主线程运行结束
}
}
打印结果(可能出现的情况):
main:join machine
t1:0
t1:1
t1:2
……
t1:55
main:end
t1:56
……
注:学习内容和代码,来源自《JAVA 面向对象编程》(第二版)作者:孙卫琴