Android多線程分析之二:Thread的實現

來源:互聯網
上載者:User

標籤:android   des   style   blog   http   java   

Android多線程分析之二:Thread的實現羅朝輝 (http://www.cnblogs.com/kesalin/)CC 許可,轉載請註明出處 

在前文《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>(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 釋放記憶體,至此線程就終結了。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.