程序中的多个线程是并发执行的,某个线程若想被执行必须要得到CPU的使用权,Java虚拟机会按照特定的机制为程序中的每个线程分配CPU的使用权,这种机制被称作线程的调度。
两种线程调度模式:
分时调度模式:让所有的线程轮流获得CPU的使用权,并且平均分配每个线程占用的CPU的时间片。
抢占式调度模式:让可运行池中优先级高的线程优先占用CPU,而对于优先级相同的线程,随机选择一个线程使其占用CPU,当它失去了CPU的使用权后,再随机选择其他线程使其占用CPU。
Java虚拟机默认采用抢占式调度模型。
一、线程的优先级
线程的优先级用1~10之间的整数来表示,数字越大优先级越高。Thread类提供三个静态常量表示线程的优先级:
MAX_PRIORITY :相当于10
MIN_PRIORITY:相当于1
NORM_PRIORITY:相当于5
通常用setPriority(int 优先级)对线程进行设置,但优先级调整的是几率,而不是将优先级低的进程挂起。setPriority 告诉JVM这个线程的优先级,但JVM是否按你请求 (请求,不是要求)办不确定,就是结果不确定(最主要原因是这些优先级需要操作系统的支持,不同的操作系统对优先级的支持是不一样的)
二、线程休眠
运用sleep()函数,使正在执行的线程暂停一段时间,将CPU让给别的线程,即让线程进入休眠等待状态。
注意:①sleep()释放资源不释放锁
②sleep(long millis)方法生命抛出InterruptedException异常,因此在调用该方法时应该捕获异常,或声明抛出该异常。
//定义SleepThread类实现Runnable接口
class SleepThread implements Runnable {
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("线程一正在输出:" + i);
}
}
}
public class Example2 {
public static void main(String[] args) throws Exception {
// 创建一个线程
new Thread(new SleepThread()).start();
for (int i = 1; i <= 10; i++) {
if (i == 5) {
System.out.println("主线程暂时休眠500毫秒,接下来是线程一输出");
Thread.sleep(2000); // 当前线程休眠2秒
}
System.out.println("主线程正在输出:" + i);
}
}
}
结果:
线程休眠只是让该线程停一段时间,一段时间之后就可以接着进行,不需要别的线程进行完,例子中因为线程一总执行时间较短,在主线程休眠的时间内就运行完了。
三、线程让步
通过yield()方法,将当前进程停下,换成就绪状态,让系统的调度器重新调度一次。
与sleep()方法相似,但yield()方法不会阻塞该线程,之后该线程与其他线程是相对公平的。调度谁看系统,有可能还是调度它自己。
class YieldThread extends Thread {
// 定义一个有参的构造方法
public YieldThread(String name) {
super(name); // 调用父类的构造方法
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
if (i == 3) {
System.out.print("线程让步:\n");
Thread.yield(); // 线程运行到此,作出让步
}
}
}
}
public class Example2 {
public static void main(String[] args) {
// 创建两个线程
Thread t1 = new YieldThread("线程A");
Thread t2 = new YieldThread("线程B");
// 开启两个线程
t1.start();
t2.start();
}
}
结果:
通过结果可以看到线程让步之后,有可能系统调度的是别的线程,有可能还是自己。
四、线程插队
就像它的名字,插队,通过join()方法,阻塞当前线程,先完成被join()方法加入的插入的这个线程,之后再完成其他线程。
class EmergencyThread implements Runnable {
public void run() {
for (int i = 1; i < 7; i++) {
System.out.println(Thread.currentThread().getName()+"输出:"+i);
}
}
}
public class Example2{
public static void main(String[] args) throws Exception {
// 创建线程
Thread t = new Thread(new EmergencyThread(),"线程一");
t.start(); // 开启线程
for (int i = 1; i < 7; i++) {
System.out.println(Thread.currentThread().getName()+"输出:"+i);
if (i == 2) {
//System.out.println("线程1插队");
t.join(); // 调用join()方法
}
}
}
}
结果:
从结果可以看到,当main线程输出2以后,线程一就开始执行,直到线程一执行完毕,main线程才继续执行。