四、线程调度
线程调度管理器负责线程排队和CPU在线程间的分配,并按线程调度算法进行调度。当线程调度管理器选中某个线程时,该线程获得 CPU资源进人运行状态。
线程调度是抢占式调度,即如果在当前线程执行过程中个更高优先级的线程进人可运行状态,则这个更高优先级的线程立即被调度执行。
3.1实现线程调度的方法
1. join( )方法
join( )方法使当前线程暂停执行,等待调用该方法的线程结束后再继续执行本线程。它被重载了3次:
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
下面通过示例具体看一下join( )方法的应用。
示例1
使用join( )方法阻塞线程。
实现步骤:
(1)定义线程类,输出5次当前线程的名称。
(2)定义测试类,使用join()方法阻塞主线程。
// 线程类
public class MyThread extends Thread(
public MyThread(string name){
super(name);
}
public void run(){
for(int i=0;i<5;i++){
//输出当前线程的名称
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
// 测试类
public class Test{
/*
* Java 程序中的public static void main( )方法是主线程的入口,
* 运行Java程序时,会先执行这个方法。
*/
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
if (i == 5) {
System.out.println("i:" + i);
// 主线程运行5次后,开始myThread的线程
MyThread myThread = new MyThread("myThread");
try {
myThread.start();
// join()方法使当前线程暂停执行,等待调用该方法的线程结束后再继续执行本线程
// 把该线程通过join()方法插入到主线程前面,否则主线程执行完毕后再执行该线程
myThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// main,主线程
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
}
// 再解释一遍流程。
// 1、主线程开始运行。
// 2、i等于5的时候,主线程暂停。
// 3、等待调用该方法的线程myThread执行完毕后继续执行主线程main。
main-0
main-1
main-2
main-3
main-4
i:5
myThread-0
myThread-1
myThread-2
myThread-3
myThread-4
main-5
main-6
main-7
main-8
main-9
在示例1中,使用join( )方法阻塞指定的线程等到另一个线程完成以后再继续执行。其中myThread.join( )表示让当前线程即主线程main加到myThread的末尾,主线程被阻塞,myThread执行完以后主线程才能继续执行。
Thread.curentThread().geName( )获取了当前线程的名称。
从线程返回数据时也经常使用到join()方法。
示例2
使用join( )方法实现两个线程间的数据传递。
实现步骤:
(1)定义线程类,为变量赋值。
(2)定义测试类。
public class MyThread extends Thread{
public String valuel;
public String value2;
public void run() {
valuel = "value1已赋值";
value2 = "value2已赋值";
}
}
public class Test {
public static void main(Stringl[] args) throws InterruptedException{
MyThread thread = new MyThread();
thread.start();
System.out.println("value1 :"+thread.value1);
System.out.printin("value2 :"+thread.value2);
}
}
// 输出结果:
valuel: null
value2: null
在示例2中,在run( )方法中已经对value1和value2赋值,而返回的却是null,发生这种情况的原因是在主线程中调用start( )方法后就立刻输出了value1和value2的值,而这里run( )方法可能还没有执行到为valuel 和value2赋值的语句。要避免这种情况的发生,需要等run( )方法执行完后才执行输出value1和value2的代码,可以使用join( )方法来解决这个问题。修改示例2的代码,在“thread.start( );”后添加”thread.join();”,修改后此处代码如下:
public class Test {
public static void main(Stringl[] args) throws InterruptedException{
MyThread thread = new MyThread();
thread.start();
// join()方法
thread.join();
System.out.println("value1 :"+thread.value1);
System.out.printin("value2 :"+thread.value2);
}
}
// 重新运行示例2,则可以得到如下输出结果:
valuel: value1 己赋值
value2: value2 已赋值
2.sleep( )方法
sleep( )方法定义语法如下:
public static void sleep(long millis)
sleep( )方法会让当 前线程睡眠(停止执行) millis毫秒,线程由运行中的状态进人不可运行状态,
睡眠时间过后线程会再进人可运行状态。
示例1
使用sleep( )方法阻塞线程。
实现步骤:
(1)定义线程。
(2)在run( )方法中使用sleep( )方法阻塞线程。
(3)定义测试类。
public class SleepThreadTest {
public static void bySec(long s) {
for (int i = 0; i < s; i++) {
try {
// 睡眠1000毫秒
Thread.sleep(1000);
System.out.println("线程---"+Thread.currentThread().getName()+" 等待---"+ (i + 1) + "秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println("please wait...");
// 让主(该)线程等等5秒再执行
SleepThreadTest.bySec(5);
System.out.println("start...");
}
}
// 运行结果
please wait...
线程---main 等待---1秒
线程---main 等待---2秒
线程---main 等待---3秒
线程---main 等待---4秒
线程---main 等待---5秒
start...
示例3的代码中,在执行主线程以后,首先输出了please wait,然后主线程等待5秒后继续执行。
3.yield( )方法
yield( )方法定义语法如下:
public static void yield()
yield( )方法可暂停当前线程执行,允许其他线程执行, 该线程仍处于可运行状态,不转为阻塞状态。此时,系统选择其他相同或更高优先级线程执行,若无其他相同或更高优先级线程,则该线程继续执行。
示例1
使用yield()方法暂停线程。
实现步骤:
(1)定义两个线程。
(2)在run( )方法中使用yield( )方法暂停线程。
(3)定义测试类。
// 线程类1
public class FirstThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("第一个线程的第" + (i + 1) + "次运行" + Thread.currentThread().getName());
// 暂停线程
Thread.yield();
}
}
}
// 线程类2
public class SecThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("第二个线程的第" + (i + 1) + "次运行" + Thread.currentThread().getName());
// 暂停线程
Thread.yield();
}
}
}
// 测试类
public class Test {
public static void main(String[] args) {
FirstThread t1 = new FirstThread();
SecThread t2 = new SecThread();
t1.start();
t2.start();
}
}
// 运行结果,两个线程类都用了yield()方法,它们都还是可运行状态,如果没有相同更高优先级线程,它们互相交替执行
第一个线程的第1次运行Thread-0
第一个线程的第2次运行Thread-0
第一个线程的第3次运行Thread-0
第一个线程的第4次运行Thread-0
第二个线程的第1次运行Thread-1
第二个线程的第2次运行Thread-1
第二个线程的第3次运行Thread-1
第二个线程的第4次运行Thread-1
第二个线程的第5次运行Thread-1
第一个线程的第5次运行Thread-0 // 又是第一个线程
在示例6中,调用了yield( )方法之后,当前线程并不是转人被阻塞状态,它可以与其他等待执行的线程竞争CPU资源,如果此时它又抢占到CPU资源,就会出现连续运行几次的情况。sleep( )方法与yield( )方法在使用时容易混淆,这两个方法之间的区别如下表所示。
sleep( )方法 | yield( )方法 |
---|---|
使当前线程进入被阻塞状态 | 将当前线程转入暂停执行状态 |
即使没有其他等待运行的线程,当前线程也会等待指定的时间 | 如果没有其他等待的线程,当前线程会马上恢复执行 |
其他等待执行的线程的机会是均等的 | 会将优先级相同或更高的线程运行 |