java线程的join方法源码分析的不解问题
天蓬老师
天蓬老师 2017-04-17 17:59:10
0
2
906

1.首先通过源码可以看到join方法的底层逻辑还是使用当前线程对象的wait方法,也知道子线程执行完业务代码后,主线程才能解除阻塞。我认为既然使用的是wait方法,必然需要notify或notifyAll来唤醒,但唤醒的机制是什么?难道使用的线程的隐式钩子方式,当线程执行完后再进行notify?

2.伪代码,按自己的理解实现join方法,不知道这样对不对?
public class JoinTest {

public static void main(String[] args) throws InterruptedException {
    ThreadTest tt=new ThreadTest();
    tt.start();
    synchronized (tt) {
        tt.wait();
    }
    System.out.println("主线程继续。。。。");
}

}

class ThreadTest extends Thread {

public void run() {
    for(int i=0;i<5;i++){
        try {
            Thread.sleep(1000);
            System.out.println("i="+i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}
//---输出结果输下----
i=0
i=1
i=2
i=3
i=4
主线程继续。。。。

天蓬老师
天蓬老师

欢迎选择我的课程,让我们一起见证您的进步~~

reply all(2)
刘奇

Your question has been answered above, notifyAll will be called when the thread ends.

The bottom layer of JVM actually uses the API provided by the OS layer to support threads. For example, UNIX-like OS generally uses pthread (Windows also has Pthread implementation). Openjdk’s hotspotVM uses pthread. Let’s take a look at the openjdk8 version of hotspotVM. The underlying actual code.

When Thread executes the start method, it will call the start0 of the native method. The bottom layer of start0 is actually encapsulated in many layers, and finally the createJavaThread method will be called. createJavaThread will call pthread_create to create a thread and execute it.

The process is roughly like this: Thread.start() -> start0() -> ... -> createJavaCreate() -> pthread_create() => threadStart() => attachThread() -&gt ; Execute Thread's run() -> detachThread() "This method will finally call Object.notifyAll".

openjdk-8-8u66-b17/jamvm-2.0.0/src/thread.c

void createJavaThread(Object *jThread, long long stack_size) {
    Thread *self = threadSelf();
    ExecEnv *ee = sysMalloc(sizeof(ExecEnv));
    Thread *thread = sysMalloc(sizeof(Thread));

    memset(ee, 0, sizeof(ExecEnv));
    memset(thread, 0, sizeof(Thread));

    thread->ee = ee;
    ee->thread = jThread;
    ee->stack_size = stack_size;

    if(!classlibCreateJavaThread(thread, jThread)) {
        sysFree(thread);
        sysFree(ee);
    return;
    }

    disableSuspend(self);

    if(pthread_create(&thread->tid, &attributes, threadStart, thread)) {
        classlibMarkThreadTerminated(jThread);
        sysFree(ee);
    enableSuspend(self);
        signalException(java_lang_OutOfMemoryError, "can't create thread");
        return;
    }

    pthread_mutex_lock(&lock);

    /* Wait for thread to start */
    while(classlibGetThreadState(thread) == CREATING)
        pthread_cond_wait(&cv, &lock);

    pthread_mutex_unlock(&lock);
    enableSuspend(self);
}

....

void *threadStart(void *arg) {
    Thread *thread = (Thread *)arg;
    Object *jThread = thread->ee->thread;

    /* Parent thread created thread with suspension disabled.                                                                                                                                 
       This is inherited so we need to enable */
    enableSuspend(thread);

    /* Complete initialisation of the thread structure, create the thread                                                                                                                     
       stack and add the thread to the thread list */
    initThread(thread, INST_DATA(jThread, int, daemon_offset), &thread);

    /* Add thread to thread ID map hash table. */
    addThreadToHash(thread);

    /* Set state to running and notify creating thread */
    signalThreadRunning(thread);

    /* Execute the thread's run method */
    executeMethod(jThread, CLASS_CB(jThread->class)->method_table[run_mtbl_idx]);

    /* Run has completed.  Detach the thread from the VM and exit */
    detachThread(thread);

    TRACE("Thread %p id: %d exited\n", thread, thread->id);
    return NULL;
}

void *detachThread(Thread *thread) {
    Object *keep_alive;
    ExecEnv *ee = thread->ee;
    Object *java_thread = ee->thread;
    Object *group = INST_DATA(java_thread, Object*, group_offset);

    /* If there's an exception pending, it is uncaught */
    if(exceptionOccurred0(ee))
        uncaughtException();

    /* Don't do anything if this is the main thread */
    if(thread->prev == NULL)
        return NULL;

    /* remove thread from thread group */
    executeMethod(group, (CLASS_CB(group->class))->
                              method_table[rmveThrd_mtbl_idx], java_thread);

    /* Remove thread from the ID map hash table */
    deleteThreadFromHash(thread);

    objectLock(java_thread);

    /* Mark the thread as terminated.  This state is used in determining                                                                                                                      
       if the thread is alive and so must be done before notifying joining                                                                                                                    
       threads.  The VM thread structure is tied to a Java-level object                                                                                                                       
       (see comment below).  The keep_alive is an object which must be                                                                                                                        
       kept alive to prevent the structure from being freed while we are                                                                                                                      
       still accessing it */
    keep_alive = classlibMarkThreadTerminated(java_thread);

    /* Notify any threads waiting on the thread object -                                                                                                                                      
        these are joining this thread 这里调用objectNotifyAll */
    objectNotifyAll(java_thread);

    objectUnlock(java_thread);

    /* Thread's about to die, so no need to enable suspend                                                                                                                                    
       afterwards. */
    disableSuspend(thread);

    /* Grab global lock, and update thread structures protected by                                                                                                                            
       it (thread list, thread ID and number of daemon threads) */
    pthread_mutex_lock(&lock);
 /* It is safe to free the thread's ExecEnv and stack now as these are                                                                                                                     
       only used within the thread.  It is _not_ safe to free the native                                                                                                                      
       thread structure as another thread may be concurrently accessing it.                                                                                                                   
       However, they must have a reference to the java level thread --                                                                                                                        
       therefore, it is safe to free during GC when the thread is determined                                                                                                                  
       to be no longer reachable. */
    sysFree(ee->stack);
    sysFree(ee);

    /* If no more daemon threads notify the main thread (which                                                                                                                                
       may be waiting to exit VM).  Note, this is not protected                                                                                                                               
       by lock, but main thread checks again */

    if(non_daemon_thrds == 0) {
        /* No need to bother with disabling suspension                                                                                                                                        
           around lock, as we're no longer on thread list */
        pthread_mutex_lock(&exit_lock);
        pthread_cond_signal(&exit_cv);
        pthread_mutex_unlock(&exit_lock);
    }

    /* Finally, clear the thread local data */
    setThreadSelf(NULL);

    TRACE("Thread %p id: %d detached from VM\n", thread, thread->id);
    return keep_alive;
}

....

伊谢尔伦

Your inference should be correct. When a thread dies, it will call its own notifyAll method
As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.
java/lang/Thread.java#1258

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template