这篇文章内容比较浅显,涉及到的源代码不多,更多是用来做一个读书笔记
我们都知道jvm的thread实现从jdk1.2之后就使用了原生线程实现方式,
对于Sun JDK来说,它的windows版和Linux版都是使用一对一的县城模型实现的
一条Java线程就映射到一条轻量级进程之中
因为android是基于linux,
按照这个推理,我们在java测试程序中跑
for (int ii = 0; ii < 10; ii++)
{
final int tmp = ii;
new Thread(new Runnable() {
@Override
public void run()
{
try {
Thread.sleep(10000);
Log.e("ashqal","end of thread! -- " + tmp);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
并且在adb shell中ps出当前的运行的进程
事实告诉我们只有一个app进程在运行,
所以在dalvik vm中并不是一条Java线程就对应一个LWP(轻量级进程)
那么Android中是如何实现的呢?
从dalvik/vm/Thread.c中可以看到
当java程序调用thread.start()时
/*
* Create a thread as a result of java.lang.Thread.start().
*
* We do have to worry about some concurrency problems, e.g. programs
* that try to call Thread.start() on the same object from multiple threads.
* (This will fail for all but one, but we have to make sure that it succeeds
* for exactly one.)
*
* Some of the complexity here arises from our desire to mimic the
* Thread vs. VMThread class decomposition we inherited. We've been given
* a Thread, and now we need to create a VMThread and then populate both
* objects. We also need to create one of our internal Thread objects.
*
* Pass in a stack size of 0 to get the default.
*
* The "threadObj" reference must be pinned by the caller to prevent the GC
* from moving it around (e.g. added to the tracked allocation list).
*/
bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
{
pthread_attr_t threadAttr;
pthread_t threadHandle;
Thread* self;
Thread* newThread = NULL;
Object* vmThreadObj = NULL;
int stackSize;
assert(threadObj != NULL);
if(gDvm.zygote) {
// Allow the sampling profiler thread. We shut it down before forking.
StringObject* nameStr = (StringObject*) dvmGetFieldObject(threadObj,
gDvm.offJavaLangThread_name);
char* threadName = dvmCreateCstrFromString(nameStr);
bool profilerThread = strcmp(threadName, "SamplingProfiler") == 0;
free(threadName);
if (!profilerThread) {
dvmThrowException("Ljava/lang/IllegalStateException;",
"No new threads in -Xzygote mode");
goto fail;
}
}
self = dvmThreadSelf();
if (reqStackSize == 0)
stackSize = gDvm.stackSize;
else if (reqStackSize < kMinStackSize)
stackSize = kMinStackSize;
else if (reqStackSize > kMaxStackSize)
stackSize = kMaxStackSize;
else
stackSize = reqStackSize;
pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
/*
* To minimize the time spent in the critical section, we allocate the
* vmThread object here.
*/
vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
if (vmThreadObj == NULL)
goto fail;
newThread = allocThread(stackSize);
if (newThread == NULL)
goto fail;
newThread->threadObj = threadObj;
assert(newThread->status == THREAD_INITIALIZING);
/*
* We need to lock out other threads while we test and set the
* "vmThread" field in java.lang.Thread, because we use that to determine
* if this thread has been started before. We use the thread list lock
* because it's handy and we're going to need to grab it again soon
* anyway.
*/
dvmLockThreadList(self);
if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) {
dvmUnlockThreadList();
dvmThrowException("Ljava/lang/IllegalThreadStateException;",
"thread has already been started");
goto fail;
}
/*
* There are actually three data structures: Thread (object), VMThread
* (object), and Thread (C struct). All of them point to at least one
* other.
*
* As soon as "VMThread.vmData" is assigned, other threads can start
* making calls into us (e.g. setPriority).
*/
dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
/*
* Thread creation might take a while, so release the lock.
*/
dvmUnlockThreadList();
ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart,
newThread);
oldStatus = dvmChangeStatus(self, oldStatus);
if (cc != 0) {
/*
* Failure generally indicates that we have exceeded system
* resource limits. VirtualMachineError is probably too severe,
* so use OutOfMemoryError.
*/
LOGE("Thread creation failed (err=%s)\n", strerror(errno));
dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, NULL);
dvmThrowException("Ljava/lang/OutOfMemoryError;",
"thread creation failed");
goto fail;
}
/*
* We need to wait for the thread to start. Otherwise, depending on
* the whims of the OS scheduler, we could return and the code in our
* thread could try to do operations on the new thread before it had
* finished starting.
*
* The new thread will lock the thread list, change its state to
* THREAD_STARTING, broadcast to gDvm.threadStartCond, and then sleep
* on gDvm.threadStartCond (which uses the thread list lock). This
* thread (the parent) will either see that the thread is already ready
* after we grab the thread list lock, or will be awakened from the
* condition variable on the broadcast.
*
* We don't want to stall the rest of the VM while the new thread
* starts, which can happen if the GC wakes up at the wrong moment.
* So, we change our own status to VMWAIT, and self-suspend if
* necessary after we finish adding the new thread.
*
*
* We have to deal with an odd race with the GC/debugger suspension
* mechanism when creating a new thread. The information about whether
* or not a thread should be suspended is contained entirely within
* the Thread struct; this is usually cleaner to deal with than having
* one or more globally-visible suspension flags. The trouble is that
* we could create the thread while the VM is trying to suspend all
* threads. The suspend-count won't be nonzero for the new thread,
* so dvmChangeStatus(THREAD_RUNNING) won't cause a suspension.
*
* The easiest way to deal with this is to prevent the new thread from
* running until the parent says it's okay. This results in the
* following (correct) sequence of events for a "badly timed" GC
* (where '-' is us, 'o' is the child, and '+' is some other thread):
*
* - call pthread_create()
* - lock thread list
* - put self into THREAD_VMWAIT so GC doesn't wait for us
* - sleep on condition var (mutex = thread list lock) until child starts
* + GC triggered by another thread
* + thread list locked; suspend counts updated; thread list unlocked
* + loop waiting for all runnable threads to suspend
* + success, start GC
* o child thread wakes, signals condition var to wake parent
* o child waits for parent ack on condition variable
* - we wake up, locking thread list
* - add child to thread list
* - unlock thread list
* - change our state back to THREAD_RUNNING; GC causes us to suspend
* + GC finishes; all threads in thread list are resumed
* - lock thread list
* - set child to THREAD_VMWAIT, and signal it to start
* - unlock thread list
* o child resumes
* o child changes state to THREAD_RUNNING
*
* The above shows the GC starting up during thread creation, but if
* it starts anywhere after VMThread.create() is called it will
* produce the same series of events.
*
* Once the child is in the thread list, it will be suspended and
* resumed like any other thread. In the above scenario the resume-all
* code will try to resume the new thread, which was never actually
* suspended, and try to decrement the child's thread suspend count to -1.
* We can catch this in the resume-all code.
*
* Bouncing back and forth between threads like this adds a small amount
* of scheduler overhead to thread startup.
*
* One alternative to having the child wait for the parent would be
* to have the child inherit the parents' suspension count. This
* would work for a GC, since we can safely assume that the parent
* thread didn't cause it, but we must only do so if the parent suspension
* was caused by a suspend-all. If the parent was being asked to
* suspend singly by the debugger, the child should not inherit the value.
*
* We could also have a global "new thread suspend count" that gets
* picked up by new threads before changing state to THREAD_RUNNING.
* This would be protected by the thread list lock and set by a
* suspend-all.
*/
dvmLockThreadList(self);
assert(self->status == THREAD_RUNNING);
self->status = THREAD_VMWAIT;
while (newThread->status != THREAD_STARTING)
pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock);
LOG_THREAD("threadid=%d: adding to list\n", newThread->threadId);
newThread->next = gDvm.threadList->next;
if (newThread->next != NULL)
newThread->next->prev = newThread;
newThread->prev = gDvm.threadList;
gDvm.threadList->next = newThread;
if (!dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon))
gDvm.nonDaemonThreadCount++; // guarded by thread list lock
dvmUnlockThreadList();
/* change status back to RUNNING, self-suspending if necessary */
dvmChangeStatus(self, THREAD_RUNNING);
/*
* Tell the new thread to start.
*
* We must hold the thread list lock before messing with another thread.
* In the general case we would also need to verify that newThread was
* still in the thread list, but in our case the thread has not started
* executing user code and therefore has not had a chance to exit.
*
* We move it to VMWAIT, and it then shifts itself to RUNNING, which
* comes with a suspend-pending check.
*/
dvmLockThreadList(self);
assert(newThread->status == THREAD_STARTING);
newThread->status = THREAD_VMWAIT;
pthread_cond_broadcast(&gDvm.threadStartCond);
dvmUnlockThreadList();
dvmReleaseTrackedAlloc(vmThreadObj, NULL);
return true;
fail:
freeThread(newThread);
dvmReleaseTrackedAlloc(vmThreadObj, NULL);
return false;
}
所以java thread和pthread是一对一的对应关系
我们知道android内核的实现是google开发的linux内核Bionic
所以我猜测bionic内核不是以轻量级进程的方式实现的进程
因为水平有限,我只做了验证
写了一个pthread测试程序,运行在android上,打印getpid后结果与主线程的getpid值一样,且与ps内显示的值相同
所以验证了上述猜测