android當前線程id
㈠ Android線程池ThreadPoolExecutor詳解
傳統的多線程是通過繼承Thread類及實現Runnable介面來實現的,每次創建及銷毀線程都會消耗資源、響應速度慢,且線程缺乏統一管理,容易出現阻塞的情況,針對以上缺點,線程池就出現了。
線程池是一個創建使用線程並能保存使用過的線程以達到復用的對象,簡單的說就是一塊緩存了一定數量線程的區域。
1.復用線程:線程執行完不會立刻退出,繼續執行其他線程;
2.管理線程:統一分配、管理、控制最大並發數;
1.降低因頻繁創建&銷毀線程帶來的性能開銷,復用緩存在線程池中的線程;
2.提高線程執行效率&響應速度,復用線程:響應速度;管理線程:優化線程執行順序,避免大量線程搶占資源導致阻塞現象;
3.提高對線程的管理度;
線程池的使用也比較簡單,流程如下:
接下來通過源碼來介紹一下ThreadPoolExecutor內部實現及工作原理。
線程池的最終實現類是ThreadPoolExecutor,通過實現可以一步一步的看到,父介面為Executor:
其他的繼承及實現關系就不一一列舉了,直接通過以下圖來看一下:
從構造方法開始看:
通過以上可以看到,在創建ThreadPoolExecutor時,對傳入的參數是有要求的:corePoolSize不能小於0;maximumPoolSize需要大於0,且需要大於等於corePoolSize;keepAliveTime大於0;workQueue、threadFactory都不能為null。
在創建完後就需要執行Runnable了,看以下execute()方法:
在execute()內部主要執行的邏輯如下:
分析點1:如果當前線程數未超過核心線程數,則將runnable作為參數執行addWorker(),true表示核心線程,false表示非核心線程;
分析點2:核心線程滿了,如果線程池處於運行狀態則往workQueue隊列中添加任務,接下來判斷是否需要拒絕或者執行addWorker();
分析點3:以上都不滿足時 [corePoolSize=0且沒有運行的線程,或workQueue已經滿了] ,執行addWorker()添加runnable,失敗則執行拒絕策略;
總結一下:線程池對線程創建的管理,流程圖如下:
在執行addWorker時,主要做了以下兩件事:
分析點1:將runnable作為參數創建Worker對象w,然後獲取w內部的變數thread;
分析點2:調用start()來啟動thread;
在addWorker()內部會將runnable作為參數傳給Worker,然後從Worker內部讀取變數thread,看一下Worker類的實現:
Worker實現了Runnable介面,在Worker內部,進行了賦值及創建操作,先將execute()時傳入的runnable賦值給內部變數firstTask,然後通過ThreadFactory.newThread(this)創建Thread,上面講到在addWorker內部執行t.start()後,會執行到Worker內部的run()方法,接著會執行runWorker(this),一起看一下:
前面可以看到,runWorker是執行在子線程內部,主要執行了三件事:
分析1:獲取當前線程,當執行shutdown()時需要將線程interrupt(),接下來從Worker內部取到firstTask,即execute傳入的runnable,接下來會執行;
分析2:while循環,task不空直接執行;否則執行getTask()去獲取,不為空直接執行;
分析3:對有效的task執行run(),由於是在子線程中執行,因此直接run()即可,不需要start();
前面看到,在while內部有執行getTask(),一起看一下:
getTask()是從workQueue內部獲取接下來需要執行的runnable,內部主要做了兩件事:
分析1:先獲取到當前正在執行工作的線程數量wc,通過判斷allowCoreThreadTimeOut[在創建ThreadPoolExecutor時可以進行設置]及wc > corePoolSize來確定timed值;
分析2:通過timed值來決定執行poll()或者take(),如果WorkQueue中有未執行的線程時,兩者作用是相同的,立刻返回線程;如果WorkQueue中沒有線程時,poll()有超時返回,take()會一直阻塞;如果allowCoreThreadTimeOut為true,則核心線程在超時時間沒有使用的話,是需要退出的;wc > corePoolSize時,非核心線程在超時時間沒有使用的話,是需要退出的;
allowCoreThreadTimeOut是可以通過以下方式進行設置的:
如果沒有進行設置,那麼corePoolSize數量的核心線程會一直存在。
總結一下:ThreadPoolExecutor內部的核心線程如何確保一直存在,不退出?
上面分析已經回答了這個問題,每個線程在執行時會執行runWorker(),而在runWorker()內部有while()循環會判斷getTask(),在getTask()內部會對當前執行的線程數量及allowCoreThreadTimeOut進行實時判斷,如果工作數量大於corePoolSize且workQueue中沒有未執行的線程時,會執行poll()超時退出;如果工作數量不大於corePoolSize且workQueue中沒有未執行的線程時,會執行take()進行阻塞,確保有corePoolSize數量的線程阻塞在runWorker()內部的while()循環不退出。
如果需要關閉線程池,需要如何操作呢,看一下shutdown()方法:
以上可以看到,關閉線程池的原理:a. 遍歷線程池中的所有工作線程;b. 逐個調用線程的interrupt()中斷線程(註:無法響應中斷的任務可能永遠無法終止)
也可調用shutdownNow()來關閉線程池,二者區別:
shutdown():設置線程池的狀態為SHUTDOWN,然後中斷所有沒有正在執行任務的線程;
shutdownNow():設置線程池的狀態為STOP,然後嘗試停止所有的正在執行或暫停任務的線程,並返回等待執行任務的列表;
使用建議:一般調用shutdown()關閉線程池;若任務不一定要執行完,則調用shutdownNow();
總結一下:ThreadPoolExecutor在執行execute()及shutdown()時的調用關系,流程圖如下:
線程池可以通過Executors來進行不同類型的創建,具體分為四種不同的類型,如下:
可緩存線程池:不固定線程數量,且支持最大為Integer.MAX_VALUE的線程數量:
1、線程數無限制
2、有空閑線程則復用空閑線程,若無空閑線程則新建線程
3、一定程度上減少頻繁創建/銷毀線程,減少系統開銷
固定線程數量的線程池:定長線程池
1、可控制線程最大並發數(同時執行的線程數)
2、超出的線程會在隊列中等待。
單線程化的線程池:可以理解為線程數量為1的FixedThreadPool
1、有且僅有一個工作線程執行任務
2、所有任務按照指定順序執行,即遵循隊列的入隊出隊規則
定時以指定周期循環執行任務
一般來說,等待隊列 BlockingQueue 有: ArrayBlockingQueue 、 LinkedBlockingQueue 與 SynchronousQueue 。
假設向線程池提交任務時,核心線程都被佔用的情況下:
ArrayBlockingQueue :基於數組的阻塞隊列,初始化需要指定固定大小。
當使用此隊列時,向線程池提交任務,會首先加入到等待隊列中,當等待隊列滿了之後,再次提交任務,嘗試加入隊列就會失敗,這時就會檢查如果當前線程池中的線程數未達到最大線程,則會新建線程執行新提交的任務。所以最終可能出現後提交的任務先執行,而先提交的任務一直在等待。
LinkedBlockingQueue :基於鏈表實現的阻塞隊列,初始化可以指定大小,也可以不指定。
當指定大小後,行為就和 ArrayBlockingQueue一致。而如果未指定大小,則會使用默認的 Integer.MAX_VALUE 作為隊列大小。這時候就會出現線程池的最大線程數參數無用,因為無論如何,向線程池提交任務加入等待隊列都會成功。最終意味著所有任務都是在核心線程執行。如果核心線程一直被占,那就一直等待。
SynchronousQueue :無容量的隊列。
使用此隊列意味著希望獲得最大並發量。因為無論如何,向線程池提交任務,往隊列提交任務都會失敗。而失敗後如果沒有空閑的非核心線程,就會檢查如果當前線程池中的線程數未達到最大線程,則會新建線程執行新提交的任務。完全沒有任何等待,唯一制約它的就是最大線程數的個數。因此一般配合Integer.MAX_VALUE就實現了真正的無等待。
但是需要注意的是, 進程的內存是存在限制的,而每一個線程都需要分配一定的內存。所以線程並不能無限個。
㈡ android裡面列印出線程的id的函數是哪個阿
getThreadId()->androidGetThreadId()-> pthread_self()或者是GetCurrentThreadId();
㈢ android java線程問題
handler.post(r)其實這樣並不會新起線程,只是執行的runnable里的run()方法,卻沒有執行start()方法,所以runnable走的還是UI線程。
1.如果像這樣,是可以操作ui,但是run還是走在主線程,見列印出來的Log線程名字是main,說明是主線程。
這就是為什麼可以直接在run方法里操作ui,因為它本質還是ui線程
handler.post(new Runnable(){
public void run(){
Log.e("當前線程:",Thread.currrentThread.getName());//這里列印de結果會是main
setTitle("哈哈");
}
});
2.通過HandlerThread獲取到looper卻是可以新起線程,但是在這里的run方法里操作ui是不可能的,但是這顯然有個缺點,如果你執行多次post(r)方法其實走的還是HandlerThread線程。假如你執行5次,n次,其實還是一次並且它們是串列的。假如下載5張圖片,你會看到圖片是下完第一張,才會去下第二張。
實踐證明,只有是擁有主線程looper的handler才可以操作ui,而在主線程操作ui可以在handler的handlerMessage()方法中操作Ui,也可以在handler的post(r)的run方法里操作Ui.
HandlerThread ht = new HandlerThread("handler thread");
ht.start();
handler = new Handler(ht.getLooper());
handler.post(new Runnable(){//這里run()方法其實還是在等ht.start()調用
public void run(){
Log.e("當前線程:",Thread.currrentThread.getName());//這里列印的會是handler thread
setTitle("哈哈");//這樣必定報錯
//Android.view.ViewRoot$: Only the original thread that created a view hierarchy can touch its views.
}
});
這樣該怎麼辦呢,呵呵,可以無參構建一個handler。用這個handler來發送消息和處理消息,用上面的handler來開啟新線程。
mainHandler = new Handler(){
protecket void handlerMessage(Message msg){
setTitle("哈哈");//這樣就不會報錯啦
}
}
handler.post(new Runnable(){//這里run()方法其實還是在等ht.start()調用
public void run(){
Log.e("當前線程:",Thread.currrentThread.getName());//這里列印的會是handler thread
mainHandler.sendEmpetMessage();//用mainHandler來發送消息
//setTitle("哈哈");//這樣必定報錯
//android.view.ViewRoot$: Only the original thread that created a view hierarchy can touch its views.
}
});
㈣ 如何在Android中取得當前進程名
方法一:調用ActivityManager
代碼如下
ActivityManageram=(ActivityManager)this
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo>list=_am.getRunningAppProcesses();
for(inti=0;i<list.size();i++){
Log.i("tag",list.get(i).pid);
}
方法二:調用Process類獲取
只需要一句代碼即可
intpid=android.os.Process.myPid();
還可以使用android.os.Process.myTid()獲取調用進程的線程ID
和android.os.Process.myUid():獲取該進程的用戶ID
㈤ android裡面列印出線程的id的函數是哪個阿
列印當前線程:
System.out.println(Thread.currentThread().getId());
㈥ android 如何得到線程id
有時候,在kernel里經常收到一些系統調用,但不知道是誰調的。可以按下面的步驟找到。
1.kernel 裡面列印出線程ID、線程名、進程ID
char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1];
printk("pid %u(%s).
",current->pid, get_task_comm(currtask_name, current));
2. 在/proc/pid/task/tid/下面有線程的信息。
另外,在user層可以通過這種方法得到線程ID.
㈦ android 中,怎麼清除一個Handler里的所有消息隊列有什麼方法么
final void removeMessages(int what)
Remove any pending posts of messages with code 'what' that are in the message queue.
這個是我看API裡面的一個方法。看解釋應該是異常指定標識what對應的那個消息msg。。希望對你有幫助
㈧ Android如何保證一個線程最多隻能有一個Looper
Looper的構造方法為private,所以不能直接使用其構造方法創建。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}1234
要想在當前線程創建Looper,需使用Looper的prepare方法,Looper.prepare()。
如果現在要我們來實現Looper.prepare()這個方法,我們該怎麼做?我們知道,Android中一個線程最多隻能有一個Looper,若在已有Looper的線程中調用Looper.prepare()會拋出RuntimeException(「Only one Looper may be created per thread」)。面對這樣的需求,我們可能會考慮使用一個HashMap,其中Key為線程ID,Value為與線程關聯的Looper,再加上一些同步機制,實現Looper.prepare()這個方法,代碼如下: