Android入門教程 | 多執行緒

Android_anzi發表於2021-11-18

作業系統可以在同一時刻執行多個程式。例如一邊播放音樂,一邊下載檔案和瀏覽網頁。作業系統將cpu的時間片分配給每一個程式,給人一種並行處理的感覺。

一個多執行緒程式可以同時執行多個任務。通常,每一個任務稱為一個執行緒(thread),它是執行緒控制的簡稱。可以同時執行一個以上執行緒的程式成為多執行緒程式(multithreaded)。

多程式和多執行緒有哪些區別呢?

本質區別在於程式每個程式有自己的一整套變數,而執行緒則共享資料。 執行緒比程式更輕量級,建立、銷燬一個執行緒比啟動新程式的開銷要小。

實際應用中,多執行緒非常有用。例如應用一邊處理使用者的輸入指令,一遍聯網獲取資料。

Thread

Thread類屬於 java.lang包。

要建立一個執行緒很簡單,新建一個 Thread物件,並傳入一個 Runnable,實現 run()方法。 呼叫 start()方法啟動執行緒。

Java

Thread t1 = new Thread(new Runnable() {    @Override
    public void run() {
        System.out.println("rustfisher said: hello");
    }
});
t1.start();

Java lambda

Thread t1 = new Thread(() -> System.out.println("rustfisher said: hello"));t1.start();
不要直接呼叫run()方法
直接呼叫 run()方法不會啟動新的執行緒,而是直接在當前執行緒執行任務。

我們來看一個使用了 Thread.sleep()方法的例子。

Thread t1 = new Thread(() -> {    for (String a : "rustfisher said: hello".split("")) {        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.print(a);
    }
});t1.start();

sleep(int)方法會讓執行緒睡眠一個指定的時間(單位毫秒)。並且需要 try-catch 捕獲 InterruptedException異常。

中斷執行緒

run()方法執行完最後一條語句後,或者 return,或者出現了未捕獲的異常,執行緒將會終止。

使用Thread的 interrupt方法也可以終止執行緒。呼叫 interrupt方法時,會修改執行緒的中斷狀態為 true。 用 isInterrupted()可以檢視執行緒的中斷狀態。

但如果執行緒被阻塞了,就沒法檢測中斷狀態。當在一個被阻塞的執行緒(sleep或者wait)上呼叫 interrupt方法,阻塞呼叫將會被 InterruptedException中斷。

被中斷的執行緒可以決定如何響應中斷。可以簡單地將中斷作為一個終止請求。比如我們主動捕獲 InterruptedException

Thread t2 = new Thread(() -> {    for (String a : "rustfisher said: hello".split("")) {        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("被中斷 退出執行緒");            return;
        }
        System.out.print(a);
    }
});t2.start();new Thread(() -> {    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    t2.interrupt();
}).start();

上面這個小例子展示了用 interrupt()來中斷執行緒 t2。而執行緒 t2run()方法中捕獲 InterruptedException後,可以進行自己的處理。

執行緒的狀態

執行緒有 6 種狀態,用列舉類 State 來表示:

  • NEW(新建立)
  • RUNNABLE(可執行)
  • BLOCKED(被阻塞)
  • WAITING(等待)
  • TIMED_WAITING(計時等待)
  • TERMINATED(被終止)

用  getState() 方法可以獲取到執行緒的狀態。

新建立執行緒

new一個執行緒的時候,執行緒還沒開始執行,此時是NEW(新建立)狀態。線上程可以執行前,還有一些工作要做。

可執行執行緒

一旦呼叫 start()方法,執行緒處於RUNNABLE(可執行)狀態。呼叫 start()後並不保證執行緒會立刻執行,而是要看作業系統的安排。

一個執行緒開始執行後,它不一定時刻處於執行狀態。作業系統可以讓其他執行緒獲得執行機會。一個可執行的執行緒可能正在執行也可能沒在執行。

被阻塞和等待

執行緒處於被阻塞和等待狀態時,它暫時不活動。不執行程式碼,且只消耗最少的資源。直到執行緒排程器重新啟用它。

  • 一個執行緒試圖獲取一個內部的物件鎖,而該鎖被其他執行緒持有,則這個執行緒進入阻塞狀態。當這個鎖被釋放,並且執行緒排程器允許這個執行緒持有它,該執行緒變成非阻塞狀態。
  • 當執行緒等待另一個執行緒通知排程器,它自己進入等待狀態。例如呼叫 Object.wait()或者 Thread.join()方法。
  • 帶有超時引數的方法可讓執行緒進入超時等待狀態。例如  Thread.sleep()Object.wait(long)Thread.join(long)Lock.tryLock(long time, TimeUnit unit)
被終止

終止的原因:

  • run方法正常退出
  • 出現了沒有捕獲的異常而終止了run方法

執行緒屬性

執行緒優先順序,守護執行緒,執行緒組以及處理未捕獲異常的處理器。

執行緒優先順序

Java中每個執行緒都有一個優先順序。預設情況下,執行緒繼承它的父執行緒的優先順序。 可用 setPriority(int)方法設定優先順序。優先順序最大為 MAX_PRIORITY = 10,最小為 MIN_PRIORITY = 1,普通的是 NORM_PRIORITY = 5。 執行緒排程器有機會選新執行緒是,會優先選高優先順序的執行緒。

守護執行緒

呼叫 setDaemon(true)可以切換為守護執行緒(daemon thread)。守護執行緒的用途是為其他執行緒提供服務。例如計時執行緒。 當只剩下守護執行緒時,虛擬機器就退出了。

守護執行緒不應該去訪問固有資源,如檔案和資料庫。

未捕獲異常處理器

run()方法裡丟擲一個未捕獲異常,線上程死亡前,異常被傳遞到一個用於未捕獲異常的處理器。 要使用這個處理器,需要實現介面 Thread.UncaughtExceptionHandler,並且用 setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)方法把它交給執行緒。

Thread t3 = new Thread(() -> {    try {        Thread.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
        return;
    }    int x = 0, y = 3;    int z = y / x; // 故意弄一個異常});
t3.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {        System.out.println(t + "有未捕獲異常");
        e.printStackTrace();
    }
});
t3.start();

執行後, run()方法裡丟擲 ArithmeticException異常

Thread[Thread-0,5,main]有未捕獲異常
java.lang.ArithmeticException: / by zero
    at Main.lambda$main$0(Main.java:15)
    at java.lang.Thread.run(Thread.java:748)

也可以用靜態方法 Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)給所有的執行緒安裝一個預設處理器。可以在這個預設處理器裡做一些工作,例如記錄日誌。

ThreadGroup 代表著一組執行緒。也可以包含另外的執行緒組。

ThreadGroup 類實現了 UncaughtExceptionHandler 介面。它的 uncaughtException(Thread t, Throwable e)方法會有如下操作

  • 如果該執行緒組有父執行緒組,則父執行緒組的 uncaughtException被呼叫。
  • 否則,如果 Thread.getDefaultUncaughtExceptionHandler()返回一個非空處理器,則使用這個處理器。
  • 否則,如果丟擲的 ThrowableThreadDeath物件,就什麼也不做。
  • 否則,執行緒的名字和 Throwable的棧蹤跡輸出到 System.err上。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70008155/viewspace-2842990/,如需轉載,請註明出處,否則將追究法律責任。

相關文章