这篇文章内容比较浅显,涉及到的源代码不多,更多是用来做一个读书笔记
我们都知道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(); }
事实告诉我们只有一个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内显示的值相同
所以验证了上述猜测