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