多线程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()方法总能返回 。