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() 的接口:

/**

* 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 中,源码注释写的很清晰,在这里就不罗嗦了:

/**

* 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 类中一些关键成员变量如下:

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;

vmThread:可视为对 dalvik thread 的简单封装,Thread 类通过 VMThread 里面的 JNI 方法来调用 dalvik 中操作线程的方法,通过它的成员变量 thread 和 vmata,我们可以将 Android Thread 和 dalvik Thread 的关联起来;

group:每一个线程都属于一个group,当线程被创建时就会加入一个特定的group,当线程运行结束,会从这个 group 中移除;

daemon:当前线程是不是守护线程,守护线程只会在没有非守护线程运行的情况下才会运行;

priority:线程优先级,Java Thread 类的线程优先级取值范围为 [1, 10],默认优先级为 5;

stackSize:线程栈大小,默认为 0,即使用默认的线程栈大小(由 dalvik 中的全局变量 gDvm.stackSize 决定);

target:一个 Runnable 对象,Thread 的 run() 方法中会转掉该 target 的 run() 方法,这是线程真正处理事务的地方;

id:Android 线程 id,通过递增 count 得到该 id,如果没有显示给线程设置名字,那么就会使用 Thread+id 当作线程的名字。注意这不是真正意义上的线程 id,即在 logcat 中打印的 tid 并不是这个 id,那 tid 是指 dalvik 线程的 id;

localValues:线程本地存储(TLS)数据;

接下来,我们来看Android Thread 的构造函数,大部分构造函数都是通过转调静态函数 create 实现的,下面来详细分析 create 这个关键函数:

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 中的代码实现的。

public static Thread currentThread() {

return VMThread.currentThread();

}

VMThread 的 currentThread 是一个 native 方法,其 JNI 实现为 android/dalvik/vm/native/java_lang_VMThread.cpp 中的 Dalvik_java_lang_VMThread_currentThread 方法:

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 中:

Thread* dvmThreadSelf()

{

return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);

}

从上面的调用栈可以看到,每一个 dalvik 线程都会将自身存放在key 为 pthreadKeySelf 的线程本地存储中,获取当前线程时,只需要根据这个 key 查询获取即可,dalvik Thread 有一个名为 threadObj 的成员变量:

/*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 中:

/**

* Called by the Thread constructor.*/

final void addThread(Thread thread) throws IllegalThreadStateException {

synchronized (threadRefs) {

if (isDestroyed) {

throw new IllegalThreadStateException();

}

threadRefs.add(new WeakReference(thread));

}

}

ThreadGroup 的代码相对简单,它有一个名为 threadRefs 的列表,持有属于同一组的 thread 引用,可以对一组 thread 进行一些线程操作。

上面分析的是 Android Thread 的构造过程,从上面的分析可以看出,Android Thread 的构造方法仅仅是设置了一些线程属性,并没有真正去创建一个新的 dalvik Thread,dalvik Thread 创建过程要等到客户代码调用 Android Thread 的 start() 方法才会进行。下面我们来分析 Java Thread 的 start() 方法:

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 方法:

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,同样只列出关键的地方:

/** 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() 方法,运行客户代码:

public void run() {

if (target != null) {

target.run();

}

}

对于继承自 Android Thread 带有 Looper 的 Android HandlerThread 来说,会调用它覆写 run 方法():(关于 Looper 的话题下一篇会讲到,这里暂且略过)

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 方法:

/** 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 释放内存,至此线程就终结了。

android c++ 线程,Android多线程分析之二:Thread的实现相关推荐

  1. Android多线程分析之二:Thread的实现

    Android多线程分析之二:Thread的实现 罗朝辉 (http://www.cnblogs.com/kesalin/) CC 许可,转载请注明出处 在前文<Android多线程分析之一:使 ...

  2. Java/Android 进程与线程之 多线程开发(二)

    多线程开发 Java多线程开发 1.线程状态 线程阻塞 线程协作 作用 **wait() / notify** **join()** **Thread.sleep()** wait 与 sleep 的 ...

  3. Android系统的JNI原理分析(二)- 数据类型转换和方法签名

    声明 前阶段在项目中使用了Android的JNI技术,在此文中做些技术知识总结. 本文参考了一些书籍的若干章节,比如<Android进阶解密-第9章-JNI原理>.<深入理解Andr ...

  4. android 组件 线程,Android UI线程和非UI线程

    UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的 ...

  5. Android签名与认证详细分析之二(CERT.RSA剖析)

    一.Android证书的格式 Android签名中证书的格式采用X.509标准的版本三,不过省略了一些内容. X.509证书格式如下图所示: 二.证书的格式示例 从Chrome浏览器中导出一个证书,打 ...

  6. Android 4.1 Netd详细分析(二)源文件/模块/基础类统领

    这篇粗略介绍-Netd文件夹下面的源文件/模块的基本功能,和主要类的组织关系. 一.文件构成 Netd文件下的源文件模块~组织以及基本实现功能: =========================== ...

  7. android fbe分析,(原创)Android FBE加密源码分析(二)

    上一篇最后讲到,dispatchCommand是通过调用runCommand来执行具体的CMD操作,这一篇会接着说明.在进行说明前,需要先了解FBE的一些内容,为什么需要这些内容呢?因为在接下来的分析 ...

  8. Android/Linux线程死锁demo分析

    1.线程死锁 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <un ...

  9. android room 线程,Android协程——RoomCoroutines-Go语言中文社区

    在Room2.1版本中提供了对协程的支持.Dao层的方法可以被suspend标记来确保他们在主线程中被执行.接下来,我们就来看看如何使用并为它写一个简单的单元测试. 为你的数据库加点suspendin ...

最新文章

  1. 影像组学视频学习笔记(12)-支持向量机(SVM)参数优化(代码)、Li‘s have a solution and plan.
  2. 关于字符编码 转自廖雪峰的官方网站,至今看到最清晰的讲解
  3. java ajax解析json数据_利用AJAX向后台servlet传JSON数据,后台利用fastjson进行解析
  4. (软件工程复习核心重点)第三章需求分析-第五节:验证软件需求
  5. hashmap是有序还是无序_说实话,你要是看完这篇 HashMap ,和面试官扯皮真的就没问题了!
  6. 前端代码部署后通过html访问,将前端项目部署到nginx上
  7. Java实战视频bilibili
  8. 提升孩子的智力从用对方法做起
  9. 【github】命令和错误小结
  10. mybatis spring maven
  11. Java实现Unicode编码和中文互转
  12. Tuxedo中间件学习
  13. 激光导航AGV为何如此受企业青睐?
  14. 快手数码手机广告要怎么投放?需要满足什么条件?
  15. From Intrusion Detection to Attacker Attribution: A Comprehensive Survey of Unsupervised Methods翻译
  16. 云服务器公网架设传奇,传奇私服服务器端在云服务器架设,全版本通用的传奇游戏架设教程...
  17. 4K视频剪辑、特效合成、调色完美硬件配置方案2018
  18. 新玺配资:市场情绪回暖 主流热点崛起
  19. 解决NoteExpress无法在Word中插入引用文献
  20. Ubuntu20.04 Deep-wine容器下载地址

热门文章

  1. 网络通信系统架构:构建分布式即时通讯(IM)系统方案详解!
  2. 支付宝小程序自定义顶部---uniapp
  3. 由被WebInspect***引发的php header()使用问题
  4. [原创]下一代Web 应用程序安全性测试工具HP WebInspect简介
  5. vue打包部署,解决更新版本清理缓存问题
  6. fx5800p编程教程_FX5800P计算器 使用方法与编程技巧
  7. Unity基础 粒子系统
  8. 【MatLab】矩阵for循环与矩阵乘法运算速度对比
  9. 记录使用 Vue 过程中遇到的一些技术点
  10. 数据库关系代数--菜鸟简易小结