Search code examples
androidmultithreadingrunnable

Android Threads: is it necessary to wait for threads to start before "join"ing them?


I am implementing an Android "Service". In its "onCreate" I want to start and wait for completion of another thread. ClientServiceLoop is a Runnable with a while(true) loop in run() with a simple return condition.

@Override
public void onCreate() {
    super.onCreate();
    mClientServiceLoopThread = new Thread(mClientServiceLoop = new ClientServiceLoop(),
            "ClientServiceLoop");
    mClientServiceLoopThread.start();
    try {
        mClientServiceLoopThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

What I am wondering is, after I call start(), is the newly spawned thread guaranteed to have already called the Runnable's run() method already? Should I wait for the thread to start before calling join()? I wasn't able to find documentation about where exactly this was guaranteed.


Putting this to the test, join() returns immediately if I don't call start(). What I am wondering is when isAlive() is actually set. I searched Android sdk but could not find where nativePeer gets set.

--

    mClientServiceLoopThread = new Thread(mClientServiceLoop = new ClientServiceLoop(),
            "ClientServiceLoop");
    boolean b = mClientServiceLoopThread.isAlive(); // false
    try {
        mClientServiceLoopThread.join(); // internally just while(isAlive)...so returns immediately
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    mClientServiceLoopThread.start();//FIXME TESTING ONLY

-- Android source

/**
 * Blocks the current Thread (<code>Thread.currentThread()</code>) until
 * the receiver finishes its execution and dies.
 *
 * @throws InterruptedException if the current thread has been interrupted.
 *         The interrupted status of the current thread will be cleared before the exception is
 *         thrown.
 * @see Object#notifyAll
 * @see java.lang.ThreadDeath
 */
public final void join() throws InterruptedException {
    synchronized (lock) {
        while (isAlive()) {
            lock.wait();
        }
    }
}


/**
 * Returns <code>true</code> if the receiver has already been started and
 * still runs code (hasn't died yet). Returns <code>false</code> either if
 * the receiver hasn't been started yet or if it has already started and run
 * to completion and died.
 *
 * @return a <code>boolean</code> indicating the liveness of the Thread
 * @see Thread#start
 */
public final boolean isAlive() {
    return (nativePeer != 0);
}

where is nativePeer set??


Solution

  • Alright, I tracked this down definitively. It seems like nativeCreate() sets the nativePeer before pthread_create is called. As pthread_create() leaves no guarantee the thread was started by the time it returns, I wasn't sure if Android acted the same way. It seems like they have handled this. So once start() is called join() will be guaranteed to wait. But join() will not wait unless start() has been called.

    https://android.googlesource.com/platform/art/+/3c50a4b4ba6d7d9369ee9a0bd6d30bf4c9c79bb0/src/thread.cc

    // Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing to
    // assign it.
    env->SetIntField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
                   reinterpret_cast<jint>(child_thread));
    ...
    int pthread_create_result = pthread_create(&new_pthread, &attr, Thread::CreateCallback, child_thread);