Reputation: 368
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??
Upvotes: 3
Views: 150
Reputation: 368
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.
// 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);
Upvotes: 0
Reputation: 38605
Technically you can call join
before calling start
.
The problem here is that a Service by default executes code on the main thread (the UI thread) of your application. Calling join
will block your UI thread and make your app completely unresponsive.
Do not do this.
You can let onCreate()
return normally after you start the thread, the Service won't be destroyed.
Upvotes: 2
Reputation: 1685
When the main thread calls mClientServiceLoopThread.join(); it will stop running and wait for the mClientServiceLoopThread thread to finish, so you can safely call start then join.
Upvotes: 1