Android多线程分析之二:Thread的实现
Android多线程分析之二:Thread的实现
在前文《Android多线程分析之一:使用Thread异步下载图像》中演示了如何使用 Thread 处理异步事务。示例中这个 Java Thread 类都是位于 Framework 层的类,它自身是通过 JNI 转调 dalvik 里面的 Thread 相关方法实现的。因此要分析 Androd 中的线程,就需要分析这两层中的与线程相关的代码,这就是本文要探讨的主题。本文将把 Framework 层中的 Java Thread 称为 Android 线程/Thread,而把 dalvik 中的 Thread 成为 dalvik 线程/Thread。
本文涉及到的 Android 源码路径:
android/libcore/luni/src/main/java/java/lang/Runnable.java
android/libcore/luni/src/main/java/java/lang/Thread.java
android/libcore/luni/src/main/java/java/lang/ThreadGroup.java
android/libcore/luni/src/main/java/java/lang/VMThread.java
android/dalvik/vm/native/java_lang_VMThread.cpp
android/dalvik/vm/Thread.cpp
首先来分析 Android Thread,这个类的源码在android/libcore/luni/src/main/java/java/lang/Thread.java,它实现了 Runnable 接口。Runnable 只有一个无参无返回值的 void run() 的接口:
1 2 3 4 5 6 7 8 9 10 11 12 | /** * Represents a command that can be executed. Often used to run code in a * different {@link Thread}. */ public interface Runnable { /** * Starts executing the active part of the class' code. This method is * called when a thread is started that has been created with a class which * implements {@code Runnable}. */ public void run(); } |
Android Thread 存在六种状态,这些状态定义在枚举 State 中,源码注释写的很清晰,在这里就不罗嗦了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /** * A representation of a thread's state. A given thread may only be in one * state at a time. */ public enum State { /** * The thread has been created, but has never been started. */ NEW, /** * The thread may be run. */ RUNNABLE, /** * The thread is blocked and waiting for a lock. */ BLOCKED, /** * The thread is waiting. */ WAITING, /** * The thread is waiting for a specified amount of time. */ TIMED_WAITING, /** * The thread has been terminated. */ TERMINATED } |
Android Thread 类中一些关键成员变量如下:
1 2 3 4 5 6 7 8 9 10 | volatile VMThread vmThread; volatile ThreadGroup group; volatile boolean daemon; volatile String name; volatile int priority; volatile long stackSize; Runnable target; private static int count = 0 ; private long id; ThreadLocal.Values localValues; |
接下来,我们来看Android Thread 的构造函数,大部分构造函数都是通过转调静态函数 create 实现的,下面来详细分析 create 这个关键函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { Thread currentThread = Thread.currentThread(); if (group == null ) { group = currentThread.getThreadGroup(); } if (group.isDestroyed()) { throw new IllegalThreadStateException( "Group already destroyed" ); } this .group = group; synchronized (Thread. class ) { id = ++Thread.count; } if (threadName == null ) { this .name = "Thread-" + id; } else { this .name = threadName; } this .target = runnable; this .stackSize = stackSize; this .priority = currentThread.getPriority(); this .contextClassLoader = currentThread.contextClassLoader; // Transfer over InheritableThreadLocals. if (currentThread.inheritableValues != null ) { inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues); } // add ourselves to our ThreadGroup of choice this .group.addThread( this ); } |
首先,通过静态函数 currentThread 获取创建线程所在的当前线程,然后将当前线程的一些属性传递给即将创建的新线程。这是通过 VMThread 转调 dalvik 中的代码实现的:
1 2 3 | public static Thread currentThread() { return VMThread.currentThread(); } |
VMThread 的 currentThread 是一个 native 方法,其 JNI 实现为 android/dalvik/vm/native/java_lang_VMThread.cpp 中的 Dalvik_java_lang_VMThread_currentThread 方法:
1 2 3 4 5 6 7 | static void Dalvik_java_lang_VMThread_currentThread( const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); RETURN_PTR(dvmThreadSelf()->threadObj); } |
该方法里的 dvmThreadSelf() 方法定义在 android/dalvik/vm/Thread.cpp 中:
1 2 3 4 | Thread* dvmThreadSelf() { return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf); } |
从上面的调用栈可以看到,每一个 dalvik 线程都会将自身存放在key 为 pthreadKeySelf 的线程本地存储中,获取当前线程时,只需要根据这个 key 查询获取即可,dalvik Thread 有一个名为 threadObj 的成员变量:
1 2 | /* the java/lang/Thread that we are associated with */ Object* threadObj; |
dalvik Thread 这个成员变量 threadObj 关联的就是对应的 Android Thread 对象,所以通过 native 方法 VMThread.currentThread() 返回的是存储在 TLS 中的当前 dalvik 线程对应的 Android Thread。
接着分析上面的代码,如果没有给新线程指定 group 那么就会指定 group 为当前线程所在的 group 中,然后给新线程设置 name,priority 等。最后通过调用 ThreadGroup 的 addThread 方法将新线程添加到 group 中:
1 2 3 4 5 6 7 8 9 10 11 | /** * Called by the Thread constructor. */ final void addThread(Thread thread) throws IllegalThreadStateException { synchronized (threadRefs) { if (isDestroyed) { throw new IllegalThreadStateException(); } threadRefs.add( new WeakReference<Thread>(thread)); } } |
ThreadGroup 的代码相对简单,它有一个名为 threadRefs 的列表,持有属于同一组的 thread 引用,可以对一组 thread 进行一些线程操作。
上面分析的是 Android Thread 的构造过程,从上面的分析可以看出,Android Thread 的构造方法仅仅是设置了一些线程属性,并没有真正去创建一个新的 dalvik Thread,dalvik Thread 创建过程要等到客户代码调用 Android Thread 的 start() 方法才会进行。下面我们来分析 Java Thread 的 start() 方法:
1 2 3 4 5 6 7 8 9 10 11 | public synchronized void start() { if (hasBeenStarted) { throw new IllegalThreadStateException( "Thread already started." ); // TODO Externalize? } hasBeenStarted = true ; VMThread.create( this , stackSize); } } |
Android Thread 的 start 方法很简单,仅仅是转调 VMThread 的 native 方法 create,其 JNI 实现为 android/dalvik/vm/native/java_lang_VMThread.cpp 中的 Dalvik_java_lang_VMThread_create 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | static void Dalvik_java_lang_VMThread_create( const u4* args, JValue* pResult) { Object* threadObj = (Object*) args[0]; s8 stackSize = GET_ARG_LONG(args, 1); /* copying collector will pin threadObj for us since it was an argument */ dvmCreateInterpThread(threadObj, ( int ) stackSize); RETURN_VOID(); } dvmCreateInterpThread 的实现在 Thread.cpp 中,由于这个函数的内容很长,在这里只列出关键的地方: bool dvmCreateInterpThread(Object* threadObj, int reqStackSize) { Thread* self = dvmThreadSelf(); ... Thread* newThread = allocThread(stackSize); newThread->threadObj = threadObj; ... Object* vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT); dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread); dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj); ... pthread_t threadHandle; int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread); /* * 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(); ... } /* * Alloc and initialize a Thread struct. * * Does not create any objects, just stuff on the system (malloc) heap. */ static Thread* allocThread( int interpStackSize) { Thread* thread ; thread = (Thread*) calloc (1, sizeof (Thread)); ... thread ->status = THREAD_INITIALIZING; } |
首先,通过调用 allocThread 创建一个名为 newThread 的 dalvik Thread 并设置一些属性,将设置其成员变量 threadObj 为传入的 Android Thread,这样 dalvik Thread 就与Android Thread 关联起来了;然后创建一个名为 vmThreadObj 的 VMThread 对象,设置其成员变量 vmData 为 newThread,设置 Android Thread threadObj 的成员变量 vmThread 为这个 vmThreadObj,这样 Android Thread 通过 VMThread 的成员变量 vmData 就和 dalvik Thread 关联起来了。
然后,通过 pthread_create 创建 pthread 线程,并让这个线程 start,这样就会进入该线程的 thread entry 运行,下来我们来看新线程的 thread entry 方法 interpThreadStart,同样只列出关键的地方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | /* * pthread entry function for threads started from interpreted code. */ static void * interpThreadStart( void * arg) { Thread* self = (Thread*) arg; std::string threadName(dvmGetThreadName(self)); setThreadName(threadName.c_str()); /* * Finish initializing the Thread struct. */ dvmLockThreadList(self); prepareThread(self); while (self->status != THREAD_VMWAIT) pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock); dvmUnlockThreadList(); /* * Add a JNI context. */ self->jniEnv = dvmCreateJNIEnv(self); /* * Change our state so the GC will wait for us from now on. If a GC is * in progress this call will suspend us. */ dvmChangeStatus(self, THREAD_RUNNING); /* * Execute the "run" method. * * At this point our stack is empty, so somebody who comes looking for * stack traces right now won't have much to look at. This is normal. */ Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run]; JValue unused; ALOGV( "threadid=%d: calling run()" , self->threadId); assert ( strcmp (run->name, "run" ) == 0); dvmCallMethod(self, run, self->threadObj, &unused); ALOGV( "threadid=%d: exiting" , self->threadId); /* * Remove the thread from various lists, report its death, and free * its resources. */ dvmDetachCurrentThread(); return NULL; } /* * Finish initialization of a Thread struct. * * This must be called while executing in the new thread, but before the * thread is added to the thread list. * * NOTE: The threadListLock must be held by the caller (needed for * assignThreadId()). */ static bool prepareThread(Thread* thread ) { assignThreadId( thread ); thread ->handle = pthread_self(); thread ->systemTid = dvmGetSysThreadId(); setThreadSelf( thread ); ... return true ; } /* * Explore our sense of self. Stuffs the thread pointer into TLS. */ static void setThreadSelf(Thread* thread ) { int cc; cc = pthread_setspecific(gDvm.pthreadKeySelf, thread ); ... } |
在新线程的 thread entry 方法 interpThreadStart 中,首先设置线程的名字,然后通过调用 prepareThread 设置线程 id 以及其它一些属性,并调用 setThreadSelf 将新 dalvik Thread 自身保存在 TLS 中,这样之后就能通过 dvmThreadSelf 方法从 TLS 中获取它。然后修改状态为 THREAD_RUNNING,并调用对应 Android Thread 的 run 方法,运行客户代码:
1 2 3 4 5 | public void run() { if (target != null ) { target.run(); } } |
对于继承自 Android Thread 带有 Looper 的 Android HandlerThread 来说,会调用它覆写 run 方法():(关于 Looper 的话题下一篇会讲到,这里暂且略过)
1 2 3 4 5 6 7 8 9 10 11 12 | public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized ( this ) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = - 1 ; } |
target 在前面已经做了介绍,它是线程真正处理逻辑事务的地方。一旦逻辑事务处理完毕从 run 中返回,线程就会回到 interpThreadStart 方法中,继续执行dvmDetachCurrentThread 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | /* * Detach the thread from the various data structures, notify other threads * that are waiting to "join" it, and free up all heap-allocated storage. * / void dvmDetachCurrentThread() { Thread* self = dvmThreadSelf(); Object* vmThread; Object* group; ... group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group); /* * Remove the thread from the thread group. */ if (group != NULL) { Method* removeThread = group->clazz->vtable[gDvm.voffJavaLangThreadGroup_removeThread]; JValue unused; dvmCallMethod(self, removeThread, group, &unused, self->threadObj); } /* * Clear the vmThread reference in the Thread object. Interpreted code * will now see that this Thread is not running. As this may be the * only reference to the VMThread object that the VM knows about, we * have to create an internal reference to it first. */ vmThread = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread); dvmAddTrackedAlloc(vmThread, self); dvmSetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread, NULL); /* clear out our struct Thread pointer, since it's going away */ dvmSetFieldObject(vmThread, gDvm.offJavaLangVMThread_vmData, NULL); ... /* * Thread.join() is implemented as an Object.wait() on the VMThread * object. Signal anyone who is waiting. */ dvmLockObject(self, vmThread); dvmObjectNotifyAll(self, vmThread); dvmUnlockObject(self, vmThread); dvmReleaseTrackedAlloc(vmThread, self); vmThread = NULL; ... dvmLockThreadList(self); /* * Lose the JNI context. */ dvmDestroyJNIEnv(self->jniEnv); self->jniEnv = NULL; self->status = THREAD_ZOMBIE; /* * Remove ourselves from the internal thread list. */ unlinkThread(self); ... releaseThreadId(self); dvmUnlockThreadList(); setThreadSelf(NULL); freeThread(self); } /* * Free a Thread struct, and all the stuff allocated within. */ static void freeThread(Thread* thread ) { ... free ( thread ); } |
在 dvmDetachCurrentThread 函数里,首先获取当前线程 self,这里获得的就是当前执行 thread entry 的新线程,然后通过其对应的 Android Thread 对象 threadObj 获取该对象所在 group,然后将 threadObj 这个 Android Thread 对象从 group 中移除;接着清除 Android 与 dalvik 线程之间的关联关系,并通知 join 该线程的其它线程;最后,设置线程状态为 THREAD_ZOMBIE,清除 TLS 中存储的线程值,并通过调用 freeThread 释放内存,至此线程就终结了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架