• android 进程/线程管理(二)----关于线程的迷思


    一:进程和线程的由来

    进程是计算机科技发展的过程的产物。

    最早计算机发明出来,是为了解决数学计算而发明的。每解决一个问题,就要打纸带,也就是打点。

    后来人们发现可以批量的设置命令,由计算机读取这些命令,并挨个执行。

    在使用的过程中,有一个问题,如果要做I/O操作,是非常耗时的,这个时候CPU是闲着的,这对于计算机资源是一个巨大的浪费。

    于是,人们发明了进程这个东西。每个程序就是一个进程,由操作系统管理,当进行复杂的耗时操作是,CPU可以调度处理其他的进程,从而是性能在整体上提高。

    线程的目的:

    当CPU调度的某个进程时,该进程正在做网络操作,这个时候,如果用户点击某个按钮,是无法及时响应的,体验非常不好。于是,比进程更“小”的调度单位出现了----线程。

    我把与用于响应的操作放在一个线程,把耗时的操作放在其他线程,这个用户可以看到界面快速的响应,没有延时的效果。

    这也是anroid等现在主流操作系统的编程范式:

    把UI操作绑定的主线程,由工作线程处理其他的任务。主要是耗时的任务。

    二:线程的启动过程

    创建和启动一个新线程,无论经过多少层的封装,最终的目的就是由操作系统提供的api来完成。

    以下就是从java thread出发,层层分析,一直到linux的pthread结束。

    Thread源码位于:

    libcorelibdvmsrcmainjavajavalangThread.java

    public class Thread implements Runnable {

    可见thread是实现了一个runnable接口。

    复制代码
    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();
    }
    复制代码

    runnable什么也没有,就是run函数。

    所以线程的根本就是 创建一个新的线程,运行run方法。

    下面我们看看创建线程的过程。

    复制代码
        public Thread() {
            create(null, null, null, 0);
        }
    
        public Thread(Runnable runnable) {
            create(null, runnable, null, 0);
        }
    复制代码

    如上所示,常见的应用中使用thread的方法就是

    a.定义一个新thread子类,实现run方法。

    b.直接传递run给thread作为参数。

    接下去我们看下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);
        }
        public static Thread currentThread() {
            return VMThread.currentThread();
        }

    VmThread源码位于:

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

    static void Dalvik_java_lang_VMThread_currentThread(const u4* args,
        JValue* pResult)
    {
        UNUSED_PARAMETER(args);
    
        RETURN_PTR(dvmThreadSelf()->threadObj);
    }

    这里有个dvmThreadSelf()方法:

    Thread* dvmThreadSelf()
    {
        return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);
    }

    可见这是一个存放在key为pthreadKeySelf的索引。

       /* the java/lang/Thread that we are associated with */
        Object*     threadObj;

    threadObj关联的就是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));
            }
        }
     

    threadRefs里面就是存放group对每一个thread的引用。

    通过以上代码分析,thread构造方法仅仅只是设置了一些线程属性,并没有创建真正的线程。

    Thread新创建的线程:

        public synchronized void start() {
            checkNotStarted();
    
            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函数很长,但是它做了最重要的一件事:

    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 threadobj.

    创建vmThreadObj名字的Vmthread对象。

        dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
        dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);

    把vmThreadObj的VM_data方法设置成newThread。

    然后设置Android Thread vmThread变量为vmThreadObj。

    这样通过vmThreadObj, Android Thread就和dalvik thread关联起来了。

    然后就是

    int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread);

    Yes,这个就是linux操作系统创建新线程的API接口!

    接下来我们分析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);
        
    }

    首先从Android Thread获得name,然后通过prepareThread设置线程的一些属性。并调用setThreadSelf方法,把dalvik thread放入TLS。

    然后执行Android Thread的run方法。

        public void run() {
            if (target != null) {
                target.run();
            }
        }

    至此,通过操作系统提供的接口,thread里面的run方法,在新线程中运行起来了!

    本文参考:

    1.《深入理解android内核设计思想》林学森

    2.《Android内核剖析》

    3.罗朝辉  http://www.cppblog.com/kesalin/archive/2014/07/11/android_thread_impl.html

    相关文章:

    android 进程/线程管理(一)----消息机制的框架

    android 进程/线程管理(三)----Thread,Looper / HandlerThread / IntentService

  • 相关阅读:
    redis集群报错:(error) MOVED 5798 127.0.0.1:7001
    20190829小记
    20181114小结记录
    遇到的面试题记录
    机器学习-KNN算法原理 && Spark实现
    机器学习-KMeans算法原理 && Spark实现
    大数据开发-生产中遇到的10个致命问题
    大数据开发-Spark-闭包的理解
    大数据开发-Spark-共享变量之累加器和广播变量
    大数据开发-Spark-RDD的持久化和缓存
  • 原文地址:https://www.cnblogs.com/deman/p/4695191.html
Copyright © 2020-2023  润新知