暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL线程join的作用和原理

践行者的脚印 2021-07-06
1387

有这样的一个面试题:

java如何让线程按照自己指定的顺序执行?

最简单的方式就是使用join。

一、join的作用

当主线程需要等待子线程执行完毕,再继续往下执行时,如果使用sleep(xxx),无法知道子线程执行的确切时间;使用wait、notify\notifyAll需要先获取到子线程run方法的锁,再调用wait,然后当子线程执行完再调用notify\notifyAll。

然而如果使用join,主线程只需要在子线程start方法后,执行子线程的join方法。(假设子线程为t)

    t.join();
    二、源码解析
    join源码
      public final synchronized void join(long millis)
      throws InterruptedException {
      long base = System.currentTimeMillis();
      long now = 0;


      if (millis < 0) {
      throw new IllegalArgumentException("timeout value is negative");
      }


      if (millis == 0) {
      while (isAlive()) {
      wait(0);
      }
      } else {
      while (isAlive()) {
      long delay = millis - now;
      if (delay <= 0) {
      break;
      }
      wait(delay);
      now = System.currentTimeMillis() - base;
      }
      }
      }
      由源码可知,join方法底层调用的是wait方法。
      如果millis大于0,并且子线程还存活,主线程就进入等待;如果子线程死亡(执行完成),主线程就继续执行join后的代码;如果子线程在millis时间内一直存活,就等待millis后,进入wait(0)状态,当子线程死亡(执行完成),主线程就继续执行join后的代码。
      三、join底层拿的是什么锁?
      刚开始学习线程时,知道需要在拿到锁之后才能调用wait方法,然后进入等待状态,当持有锁对象的线程执行notify\notifyAll,就进入锁池队列,当获取到锁,就重新回到可运行状态。
      那么问题是,主线程调用子线程的join方法时,需要先拿到锁,这个锁是什么锁?
      猜测是子线程对象锁,而不是子线程run方法用的锁。因为如果是后者,主线程的调用会干扰子线程及其它线程的执行。
      设计了一个验证案例,如下:
      代码的含义是:有线程A\B\C,线程B拿到线程A对象的锁,然后睡2s,再执行,线程C在run方法中调用线程A的join方法。
        public class JoinTest {
        public static void main(String[] args) {


        MethodClass methodClass = new MethodClass();
        Thread threadA = new Thread(new Runnable() {
        @Override
        public void run() {
        synchronized (methodClass){
        methodClass.methodA();
        }
        }


        },"线程A");


        Thread threadB = new Thread(new Runnable() {
        @Override
        public void run() {
        synchronized (threadA){
        try {
        //线程B拿到线程A的锁,然后睡2s
        Thread.sleep(2000);
        } catch (InterruptedException e) {
        e.printStackTrace();
        }
        methodClass.methodB();
        }
        }


        },"线程B");


        Thread threadC = new Thread(new Runnable() {
        @Override
        public void run() {
        try {
        threadA.join();
        } catch (InterruptedException e) {
        e.printStackTrace();
        }
        methodClass.methodC();
        }
        },"线程C");


        threadA.start();
        threadB.start();
        threadC.start();
        }
        }


        class MethodClass{
        public void methodA(){
        System.out.println(new Date() +"::"+Thread.currentThread().getName()+",哈哈哈哈哈哈");
        }
        public void methodB(){
        System.out.println(new Date()+"::"+Thread.currentThread().getName()+",呵呵呵呵呵呵");
        }
        public void methodC(){
        System.out.println(new Date()+"::"+Thread.currentThread().getName()+",嘿嘿嘿嘿嘿嘿");
        }
        }
        如果join方法拿到的是线程对象的锁,那么线程C肯定要等到线程B执行完,并释放锁才能执行。执行顺序是A--->B--->C
        如果join方法拿到的是其它的锁,那么线程C在线程A执行完之后直接执行,而不会等线程B释放线程A对象的锁之后再执行。
          Mon Feb 01 10:32:18 CST 2021::线程A,哈哈哈哈哈哈
          Mon Feb 01 10:32:20 CST 2021::线程B,呵呵呵呵呵呵
          Mon Feb 01 10:32:20 CST 2021::线程C,嘿嘿嘿嘿嘿嘿

          通过执行结果可知:join方法拿到的是线程对象的锁

          四、最后
          通过查看相关资料知:
          join方法显示调用wait方法,隐式调用notifyAll方法。jvm底层再销毁一个对象时,会调用该对象的notifyAll方法,通知所有等待该对象锁的线程


          最后修改时间:2021-07-07 17:08:00
          文章转载自践行者的脚印,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论