美文网首页
Java并发问题-join的实现原理

Java并发问题-join的实现原理

作者: DoubleFooker | 来源:发表于2019-10-09 14:56 被阅读0次

当我们想要线程按照一定的执行顺序执行,通常最简单直接的方式就是使用到join方法。
join属于对象方法,通过线程实例可以调用。他的作用是阻塞等待线程执行结束,再执行后续的代码。接下来展开说明join的实现原理。

线程的waiting状态

JAVA中能时线程进入waiting的方法有obj.wait(),thread.join(),LockSupport.park()。waiting状态的线程需要被唤醒转化为runnable状态,等待CPU调度。而唤醒线程的方法通过执行notify/notifyAll方法,JVM调用操作系统命令实现。
我们来看下join方法

  public final void join() throws InterruptedException {
        join(0);
    }

    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");
        }
// 参数为0 调用
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
// 实际上调用的是Object的wait()方法
public final native void wait(long timeout) throws InterruptedException;

可以知道,join方法是通过不断的循环调用wait()方法是实现等待,那么什么时候会唤醒线程继续执行?join的作用是等待线程执行完成,那么可以猜想,再线程结束执行时是否会唤醒线程?
我们看下Thread的start方法,实际上调用的是native方法start0(),这时需要看下JDK源码下Thread.c找到start0方法定义

    {"start0",           "()V",        (void *)&JVM_StartThread},

再查看hotspot源码,跟踪到jvm.cpp

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  // We cannot hold the Threads_lock when we throw an exception,
  // due to rank ordering issues. Example:  we might need to grab the
  // Heap_lock while we construct the exception.
  bool throw_illegal_thread_state = false;

  // We must release the Threads_lock before we can post a jvmti event
  // in Thread::start.
  {
    // Ensure that the C++ Thread and OSThread structures aren't freed before
    // we operate.
    MutexLocker mu(Threads_lock);

    // Since JDK 5 the java.lang.Thread threadStatus is used to prevent
    // re-starting an already started thread, so we should usually find
    // that the JavaThread is null. However for a JNI attached thread
    // there is a small window between the Thread object being created
    // (with its JavaThread set) and the update to its threadStatus, so we
    // have to check for this
    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // Allocate the C++ Thread structure and create the native thread.  The
      // stack size retrieved from java is signed, but the constructor takes
      // size_t (an unsigned type), so avoid passing negative values which would
      // result in really large stacks.
      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);

      // At this point it may be possible that no osthread was created for the
      // JavaThread due to lack of memory. Check for this situation and throw
      // an exception if necessary. Eventually we may want to change this so
      // that we only grab the lock if the thread was created successfully -
      // then we can also do this check and throw the exception in the
      // JavaThread constructor.
      if (native_thread->osthread() != NULL) {
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread);
      }
    }
  }

  if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread != NULL, "Starting null thread?");

  if (native_thread->osthread() == NULL) {
    // No one should hold a reference to the 'native_thread'.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }
// 跟踪这个方法 hotspot->thread.cpp
  Thread::start(native_thread);

JVM_END

thread.cpp:

void Thread::start(Thread* thread) {
  trace("start", thread);
  // Start is different from resume in that its safety is guaranteed by context or
  // being called from a Java method synchronized on the Thread object.
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      // Initialize the thread state to RUNNABLE before starting this thread.
      // Can not set it after the thread started because we do not know the
      // exact thread state at that time. It could be in MONITOR_WAIT or
      // in SLEEPING or some other state.
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
// 跟踪hotspot os.cpp
    os::start_thread(thread);
  }
}

os.cpp:

void os::start_thread(Thread* thread) {
  // guard suspend/resume
  MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
  OSThread* osthread = thread->osthread();
  osthread->set_state(RUNNABLE);
  // 启动的方法  
  pd_start_thread(thread);
}

执行启动线程命令,则我们可以猜想下,再线程结束的时候是否会唤醒线程。跟踪到线程结束的地方(由于hotspot源码调用关系较多,省略跟踪过程)。最终调用的方法是hotspot\src\os\windows\vm\os_windows.cpp#os::pd_start_thread,linux对应os_linux.cpp

// windows
void os::pd_start_thread(Thread* thread) {
// 调用操作系统回复线程命令唤醒
  DWORD ret = ResumeThread(thread->osthread()->thread_handle());
  // Returns previous suspend state:
  // 0:  Thread was not suspended
  // 1:  Thread is running now
  // >1: Thread is still suspended.
  assert(ret != SYS_THREAD_ERROR, "StartThread failed"); // should propagate back
}

//linux
void os::pd_start_thread(Thread* thread) {
  OSThread * osthread = thread->osthread();
  assert(osthread->get_state() != INITIALIZED, "just checking");
  Monitor* sync_with_child = osthread->startThread_lock();
  MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
// notify 调用操作系统命令唤醒线程
  sync_with_child->notify();
}

可知join命令实质上是通过,wait/notify操作实现的线程等待。

相关文章

网友评论

      本文标题:Java并发问题-join的实现原理

      本文链接:https://www.haomeiwen.com/subject/itqvpctx.html