zoukankan      html  css  js  c++  java
  • Android多线程分析之二:Thread的实现

    Android多线程分析之二:Thread

    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:线程 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 方法非常easy,仅仅是转调 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 主题theme说明 摘记
    Android开发 去掉标题栏方法 摘记
    安卓项目五子棋代码详解(二)
    关于 ake sure class name exists, is public, and has an empty constructor that is public
    百度地图3.0实现图文并茂的覆盖物
    android onSaveInstanceState()及其配对方法。
    关于集成科大讯飞语音识别的 一个问题总结
    android 关于 webview 控制其它view的显示 以及更改view数据失败的问题总结
    C# 解析 json Newtonsoft果然强大,代码写的真好
    c#数据类型 与sql的对应关系 以及 取值范围
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7079979.html
Copyright © 2011-2022 走看看