1. 程式人生 > >android 程序/執行緒管理(二)----關於執行緒的迷思

android 程序/執行緒管理(二)----關於執行緒的迷思

一:程序和執行緒的由來

程序是計算機科技發展的過程的產物。

最早計算機發明出來,是為了解決數學計算而發明的。每解決一個問題,就要打紙帶,也就是打點。

後來人們發現可以批量的設定命令,由計算機讀取這些命令,並挨個執行。

在使用的過程中,有一個問題,如果要做I/O操作,是非常耗時的,這個時候CPU是閒著的,這對於計算機資源是一個巨大的浪費。

於是,人們發明了程序這個東西。每個程式就是一個程序,由作業系統管理,當進行復雜的耗時操作是,CPU可以排程處理其他的程序,從而是效能在整體上提高。

執行緒的目的:

當CPU排程的某個程序時,該程序正在做網路操作,這個時候,如果使用者點選某個按鈕,是無法及時響應的,體驗非常不好。於是,比程序更“小”的排程單位出現了----執行緒。

我把與用於響應的操作放在一個執行緒,把耗時的操作放在其他執行緒,這個使用者可以看到介面快速的響應,沒有延時的效果。

這也是anroid等現在主流作業系統的程式設計正規化:

把UI操作繫結的主執行緒,由工作執行緒處理其他的任務。主要是耗時的任務。

二:執行緒的啟動過程

建立和啟動一個新執行緒,無論經過多少層的封裝,最終的目的就是由作業系統提供的api來完成。

以下就是從java thread出發,層層分析,一直到linux的pthread結束。

Thread原始碼位於:

libcore\libdvm\src\main\java\java\lang\Thread.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 程序/執行管理----關於執行

一:程序和執行緒的由來 程序是計算機科技發展的過程的產物。 最早計算機發明出來,是為了解決數學計算而發明的。每解決一個問題,就要打紙帶,也就是打點。 後來人們發現可以批量的設定命令,由計算機讀取這些命令,並挨個執行。 在使用的過程中,有一個問題,如果要做I/O操作,是非常耗時的,這個時候CPU是閒著的

執行管理獲取和設定執行資訊

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 獲取和設定執行緒資訊 Thread類的物件中儲存了一些屬性資訊能夠幫助我們來辨別每一個執行緒,知道它的狀態,調整控制其優

作業系統之程序—臨界區管理

1.臨界區管理  臨界區:併發程序中與共享變數有關的程式段  臨界資源:共享變數代表的資源 2.臨界區解決互斥問題 如果能保證程序在臨界區執行時,不讓另一個程序進入臨界區,即各程序對共享變數的訪問是互斥的,就不會造成與時間有關的錯誤 3.臨界區的排程原則 一次至

執行管理執行的中斷

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 執行緒的中斷 一個多個執行緒在執行的Java程式,只有當其全部的執行緒執行結束時(更具體的說,是所有非守護執行緒結束或者

執行管理執行的睡眠和恢復

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 執行緒的睡眠與恢復 有時, 你會感興趣在一段確定的時間內中斷執行執行緒。例如, 程式的一個執行緒每分鐘檢查反應器狀態。其

執行管理執行的建立和執行

宣告:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 譯者:鄭玉婷 校對:歐振聰 執行緒的建立和執行 在這個指南中,我們將學習如何在Java程式中建立和執行執行緒。與每個Java語言中的元素一樣,執行緒

Android AccountManager帳號管理

Android AccountManager 帳號管理(一)分享瞭如何將自己的帳號體系註冊到系統服務AccountManagerService,統一由AccountManager來管理,這僅是自己的一點理解;但開發者對接的工作遠不止如此,還有使用者登入完成後如何

C++多執行系列執行互斥

首先了解一下執行緒互斥的概念,執行緒互斥說白了就是在程序中多個執行緒的相互制約,如執行緒A未執行完畢,其他執行緒就需要等待! 執行緒之間的制約關係分為間接相互制約和直接相互制約。 所謂間接相互制約:一個系統中的多個執行緒必然要共享某種系統資源如共享CPU,共享印表機。間接制

Android Sprd省電管理應用省電模式設定流程

在Android Sprd省電管理(一)appPowerSaveConfig.xml,我們介紹了appPowerSaveConfig.xml的主要引數的意義,這一篇我們介紹下,怎麼設定應用的各種省電模式 首先看SprdManageApplications這個類 以鎖屏清理為例,點選開關

執行程式設計——執行結束後的處理&主服務存活方法

應用場景: 伺服器,建立了多個服務子執行緒,而後主執行緒“無所事事”,程序會被關閉,導致子執行緒sleepA和sleepB無法正常執行完成。(假設sleepA()和sleepB()都是沉睡若干秒的函式,這個肯定要比程式執行到main結束那幾步所花費時間要長) int ma

程序管理實驗——POSIX下執行控制

實驗目的 1、通過觀察、分析實驗現象,深入理解執行緒及執行緒在排程執行和記憶體空間等方面的特點,並掌握執行緒與程序的區別。 2、掌握在POSIX 規範中pthread_create() 函式的功能和

android 程序/執行管理----訊息機制的框架

一:android 程序和執行緒 程序是程式執行的一個例項。android通過4大主件,弱化了程序的概念,尤其是在app層面,基本不需要關係程序間的通訊等問題。 但是程式的本質沒有變,尤其是多工系統,以事件為驅動的軟體系統基本模式都是如下: 程式的入口一般是main: 1.初始化: 比如建立視窗,申

android 程序/執行管理----Thread,Looper / HandlerThread / IntentService

Thread,Looper的組合是非常常見的組合方式。 Looper可以是和執行緒繫結的,或者是main looper的一個引用。 下面看看具體app層的使用。 首先定義thread: package com.joyfulmath.androidstudy.thread; import co

Android 有效地展示圖片Processing Bitmaps Off the UI Thread 在ui執行外處理bitmap

原文連結http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html 我們在上節課討論了BitmapFactory.decode系列的方法,但是如果原圖的資料需要從硬碟或者網路或者別的途徑而非記憶

【C++併發實戰】執行管理

前一篇沒用markdown編輯器感覺不好看,刪了重新發 本篇主要講述執行緒的管理,主要包括建立和使用執行緒 啟動執行緒 執行緒出現是為了執行任務,執行緒建立時會給一個入口函式,當這個函式返回時,該執行緒就會退出,最常見的main()函式就是主執行緒的入口函式,在main()函式返回時主執行緒就結束了。 如

Android ContentProvider的執行安全

框架層原理 從Provider使用方(Client App)通過getContentResolver()獲取到Provider的遠端物件,是一個三方的流程: (1)Client App向AMS獲取Provider遠端物件; (2)AMS會檢查對應的Prov

程序管理yum

yum configure createrepo 編譯安裝 yum倉庫 一、yum簡介 二、客戶端配置文件 三、yum命令使用 四、*.repo的配置文件變量 五、使用本地光盤當做yum倉庫 六、創建yum倉庫 七、程序包的編譯安裝 八、開源程序的源代碼的獲取與安裝 九、configure

三、處理機管理--程序的排程與管理

程序的排程與管理 程序控制塊佇列(PCB),作業系統採用連結串列的方法將這些程序的PCB連結起來生成佇列。 對於單CPU系統,生成的PCB佇列如下。 (1)執行佇列。任何時刻系統中最多隻有一個程序處於執行狀態。 (2)就緒佇列。就緒佇列中的PCB會根據某種

java多執行

一,鎖 在物件的建立時java會為每個object物件分配一個monitor( 監視器或者監視鎖),當某個物件的同步方法(synchronized methods )被多個執行緒呼叫時,該物件的monitor將負責處理這些訪問的併發獨佔要求。 當一個執行緒呼叫一個物件的同步方法時(sy

JAVA執行總結

繼續上篇的總結,這次我們講執行緒同步機制 執行緒同步是為了確保執行緒安全,所謂執行緒安全指的是多個執行緒對同一資源進行訪問時,有可能產生資料不一致問題,導致執行緒訪問的資源並不是安全的。如果多執行緒程式執行結果和單執行緒執行的結果是一樣的,且相關變數的值與預期值一樣,則是執行緒安全的。