多线程07--yield和join
前言:
yield和join皆为Thread提供的基本方法,供开发者在不同场景下应用。
1. yield
- 创建线程类实现Runnable接口,代码如下:
package yield;
public class MYieldThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始执行...");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "执行完毕....");
}
}
- 主测试类:
package yield;
public class TestYield {
public static void main(String[] args) {
MYieldThread yieldThread = new MYieldThread();
new Thread(yieldThread, "111").start();
new Thread(yieldThread, "222").start();
}
}
- 执行结果(说明:礼让不一定会成功,因为具体要看CPU调度,可能A线程开启礼让并释放CPU使用权,但是CPU再次调度从队列中获取A线程继续执行):
礼让成功结果:
礼让失败结果:
总结:
Thread.yield()方法作用是暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。yield()做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间。
2. join
2.1使用join()方法控制线程执行顺序
调用Thread的start()方法启动线程时,线程的执行顺序是不确定的。也就是说,在同一个方法中,连续创建多个线程后,调用线程的start()方法的顺序并不能决定线程的执行顺序。例如如下程序:
public class ThreadStartOrderTest {
public static void main(String[] args) {
Thread thread1 = new Thread(()->{
System.out.println("this is thread1");
});
Thread thread2 = new Thread(()->{
System.out.println("this is thread2");
});
Thread thread3 = new Thread(()->{
System.out.println("this is thread3");
});
thread1.start();
thread2.start();
thread3.start();
}
}
多次执行,发现执行顺序并不是按照thread1->thread2->thread3这个启动顺序排列,而是随机的:
而在很多实际业务场景中,有时因为数据依赖等原因,需要控制线程的执行顺序,这时使用Thread类的join()方法就可以确保线程的执行顺序。更改后的代码如下:
public class ThreadStartOrderTest {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()->{
System.out.println("this is thread1");
});
Thread thread2 = new Thread(()->{
System.out.println("this is thread2");
});
Thread thread3 = new Thread(()->{
System.out.println("this is thread3");
});
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
thread3.join();
}
}
多次执行,发现输出结果均一致:
2.2 join()如何保证线程执行顺序
进入到Thraed.join()方法内,看到内部实际调用的是一个带参数的join方法:
public final synchronized void join(long millis) 源码如下:
因为传入的millis为0,所以会走如下分支,同时,该方法是一个被synchronized 修饰的同步方法,同一时刻只能被一个线程调用。
在内部调用的是Object的wait()方法,注意Object的wait/notify/notifyall方法也只能在同步代码块中使用,如果当前线程不是锁的持有者,该方法抛出一个
IllegalMonitorStateException
异常。
![]()
接着跟wait(0),它是Object类提供的一个native本地方法,通过JNI的方式调用JDK底层的方法来使线程等待执行完成。
如果main线程中调用另一个线程thread1.join(),main将会被挂起,直到目标线程thread1结束才恢复(即t.isAlive返回为假);也可以在调用join()时带上一个超时参数,这样如果目标线程在这段时间内还没有结束的话,join()方法总能返回 。