1. 程式人生 > >Android的程序、執行緒與優先順序

Android的程序、執行緒與優先順序

一、結論

1、Android中程序的優先順序與垃圾回收機制相關,優先順序越低被垃圾回收的機會越大。當記憶體不足的時候,總是低優先順序的程序被最先回收;

2、Android中執行緒的優先順序與呼叫順序有關,優先順序越高被呼叫的可能性越高(注意,是可能性更高),也就是說即使執行緒A的優先順序大於執行緒B,同等情況下執行緒A不一定先於執行緒B被呼叫。

二、程序與執行緒

1、什麼是程序、執行緒

如果你想要一個程式執行得快,那麼可以將其斷開為多個片段,在單獨的處理器上執行每個片段,這是併發程式設計最主要的考慮,簡單理解:

  • 程序(process):是一塊包含了某些資源的記憶體區域,是作業系統的最小可執行單元。作業系統利用程序把它的工作劃分為一些功能單元。一個程式至少對應一個程序。
  • 執行緒(thread):是程序的一個實體,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位。一個程序至少對應一個執行緒。
    這裡寫圖片描述

在Android中,每個app執行時前首先建立一個程序,該程序是由Zygote fork出來的,用於承載App上執行的各種Activity/Service等元件。大多數情況一個App就執行在一個程序中,除非:

(1)配置Android:process屬性:

a.程序名為com.goodong.com.process1:

    <activity
        android:name=".MyFirstActivity"
        android:process="com.goodong.com.process1"
        >
        <intent-filter>
            ……
        </intent-filter>
    </activity>

b.配置程序名為”:process2”(包名+process2):

    <activity
        android:name=".MyFirstActivity"
        android:process=":process2"
        >
        <intent-filter>……
            </intent-filter>
   </activity>

注意:第2種命名程序的方式與第1中命名方式的區別在於,後者不能做到:讓不同應用程式的元件執行在同一個程序中。

(2)通過native程式碼fork程序:

應用程式的程序是由Zygote建立的,在ActivityManagerService中的startProcessLocked中呼叫了Process.start()方法。並通過連線呼叫Zygote的native方法forkAndSpecialize,執行fork任務。

2、執行緒的排程

執行緒對應用來說非常常見,比如每次new Thread().start都會建立一個新的執行緒。該執行緒與App所在程序之間資源共享,從Linux角度來說程序與執行緒除了是否共享資源外,並沒有本質的區別。

Java的執行緒機制是搶佔式的,這表示排程機制會週期性地中斷執行緒,將上下文切換到另一個執行緒,從而為每個執行緒都提供時間片,使得每個執行緒都會分配到數量合理的時間去執行它的任務

CPU輪流為每個任務分配其佔用時間。每個任務都覺得自己在一直佔用CPU,但事實上CPU時間是劃分成片段分配給了所有任務。使用執行緒機制是一種建立透明的、可擴充套件的程式的方法,如果程式執行太慢,為機器新增一個CPU就能很容易地加快程式的執行速度。

請注意,儘管多工和多執行緒往往是使用多處理器系統的最合理方式,但多執行緒程式設計並不是僅僅針對多處理器,即使在單處理上,併發程式設計也是有用武之地的。這是因為,如果一個程式包含了多個順序執行的任務(不是併發執行),因為每個任務都可能被阻塞,一旦任務被阻塞,程式就停止執行。但在併發程式設計下,多個任務併發執行,單任務阻塞並不會直接導致程式停止執行

三、Android程序的優先順序

1、優先順序意味著什麼

優先順序的意思是:針對多個物件的某種操作的執行順序。上面我們說過,程序是一塊記憶體區域,因為對程序而言,優先順序意味著何時釋放資源:

  • 在釋放程序資源的時候,讓優先順序低的程序先釋放釋放資源;
  • 如果即將被執行的程序的優先順序比正在執行的程序的優先順序高,則系統可以強行剝奪正在執行的程序的資源,讓優先順序高的程序先執行。

2、有多少個優先順序

在Android 中,程序的優先順序就是oom_adj的值,而oom_adj被定義在init.rc中:

· Define the memory thresholds at which the above process classes will
· be killed.  These numbers are in pages (4k).
    setprop ro.FOREGROUND_APP_ADJ         0
    setprop ro.VISIBLE_APP_ADJ            1
    setprop ro.SECONDARY_SERVER_ADJ       2
    setprop ro.HIDDEN_APP_MIN_ADJ         7
    setprop ro.CONTENT_PROVIDER_ADJ       14
    setprop ro.EMPTY_APP_ADJ              15

名稱 oom_adj 解釋:
這裡寫圖片描述

3、oom_adj值會隨著程序的狀態變化而變化

adb連線手機後,筆者從桌面上啟動了知乎,用adb shell dumpsys activity檢視activitys的分佈,可以看到activity的次序如下:
這裡寫圖片描述
這時候另開一個命令列視窗檢視程序的程序號:
這裡寫圖片描述
這個時候知乎有兩個程序,分別是15345和15725,使用cat /proc//oom_adj 命令檢視它們的oom_adj值:
它們的值分別是0和11(oom_adj值是可以修改);
這裡寫圖片描述
將知乎退到後臺再查詢它們的oom_adj值:
兩個程序的值分別是9和13(退後臺前是0和11)。
這裡寫圖片描述

4、如何根據oom_adj的值判斷回收時機

init.rc中定義垃圾回收的閾值:

· Write value must be consistent with the above properties.
· Note that the driver only supports 6 slots, so we have combined some of
· the classes into the same memory level; the associated processes of higher
· classes will still be killed first.
·寫入的值必須符合上面的屬性。注意裝置只支援6個等級,所以某些
·類會被合併到同一個等級中。擁有更高等級的程序將被優先殺死。
&nbsp; &nbsp; setprop ro.FOREGROUND_APP_MEM     1536(6M)
&nbsp; &nbsp; setprop ro.VISIBLE_APP_MEM           2048(8M)
&nbsp; &nbsp; setprop ro.SECONDARY_SERVER_MEM    4096(16M)
&nbsp; &nbsp; setprop ro.HIDDEN_APP_MEM           5120(20M)
&nbsp; &nbsp; setprop ro.CONTENT_PROVIDER_MEM    5632(22M)
&nbsp; &nbsp; setprop ro.EMPTY_APP_MEM            6144(24M)

這些數字也就是對應的記憶體閾值,一旦低於該值,Android便開始按順序關閉相應的程序 。具體的回收實現在ActivityManagerService.java中的函式trimApplications():

  • 首先移除package被移走的無用程序;

基於程序當前狀態,更新oom_adj值,然後進行以下操作:

  • 移除沒有activity在執行的程序。如果APP已經儲存了所有的activity狀態,結束這個APP;
  • 最後,如果目前還是有很多activities 在執行,那麼移除那些activity狀態已經儲存好的activity。

當系統記憶體短缺時Android的Low Memory Killer根據需要殺死程序釋放其記憶體,簡單說就是尋找一個最合適的程序殺死,從而釋放它佔用的記憶體,最合適指的是:

  • oom_adj越大
  • 佔用實體記憶體越多

四、Android執行緒的優先順序

1、執行緒的簡單示範

從CPU的角度看,Android執行緒就是Java執行緒。因而Android執行緒的優先順序就是Java執行緒的優先順序(但在Android開發中,筆者似乎從未操心過執行緒的優先順序)。
Java中使用執行緒最常用的方法是:

  • 實現Runnable介面,覆寫run()方法;
  • 繼承Thread類,覆寫run()方法(實際也是實現Runnable介面)。
    先實現實現Runnable介面:

    public class RunabbleImp implements Runnable{
        @Override
    public void run() {
    doSomeThing();
    }
    private void doSomeThing() {
    System.out.println(Thread.currentThread().getName()+" is running!");
        }
    }
    

測試一下:

    public class ThreadTest {
        public static void main(String[] args) {
            RunabbleImp runabbleImp = new RunabbleImp();
            runabbleImp.run();
            Thread thread = new Thread(new RunabbleImp());
            thread.start();
        }
    }

列印結果:

    main is running!
    Thread-0 is running!

可以看出runabbleImp.run();實際上是在主執行緒中執行,而thread.start();已經開啟了一個新執行緒。

2、執行緒的優先順序

執行緒的優先順序將執行緒的重要性傳遞給排程器。儘管CPU處理現有執行緒集的順序是不確定的,但是排程器將傾向於讓優先順序高的執行緒先執行。然而,這並不意味著優先權低的執行緒將得不到執行(也就是說,優先順序不會導致死鎖)。優先順序較低的執行緒僅僅是執行的頻率較低。
可以用getPriority()獲取當前執行緒的優先順序,並且在任何時刻都可以通過setPriority()來修改它:

public class PriorityThread implements Runnable {
        private int timeCount = 5;
        private int priority;
    public PriorityThread(int priorityIn) {
    priority = priorityIn;
    }
@Override
public String toString() {
    return Thread.currentThread() +":"+timeCount;
}
@Override
public void run() {
    Thread.currentThread().setPriority(priority);
    while(true){
        for (int i = 0; i < 100000; i++) {
            double d = (Math.PI + Math.E)/(double)i;
            if(i % 1000 == 0){
                Thread.yield();
            }
        }
        System.out.println(this);
        if(--timeCount ==0){
            break;
        }
    }
}


public class ThreadTest {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            exec.execute(new PriorityThread(Thread.MIN_PRIORITY + i));
        }
    }
    exec.execute(new PriorityThread(Thread.MAX_PRIORITY));
    exec.shutdown();    
}

部分列印結果如下:

        ……
        Thread[pool-1-thread-6,10,main]:1
        ……
        Thread[pool-1-thread-5,5,main]:1
        ……
        Thread[pool-1-thread-3,3,main]:1
        Thread[pool-1-thread-1,1,main]:1
        ……
        Thread[pool-1-thread-2,2,main]:1
        Thread[pool-1-thread-4,4,main]:1

筆者執行多次後結果顯示Thread[pool-1-thread-6,10,main]總是最先執行完畢,其他執行緒執行完畢順序大致上是優先順序高的先執行完畢,但是無法保證。