並發編程之線程屬性

  • 2019 年 10 月 4 日
  • 筆記

  本文主要討論線程的各種屬性,包括:主線程、線程優先級、守護線程、線程組、處理未捕獲異常的處理器。

 
主線程
 
  任何一個Java程序啟動時,至少會啟動一個線程執行main方法,這個線程就被稱為主線程(main thread)。它是產生其它線程的線程,即:其它所有線程的主線程的子孫線程。通常,主線程都是最後結束的,因為它要執行其它子線程的關閉工作。

 
線程優先級
 
  計算機只有一個CPU,各個線程輪流獲取CPU的使用權,才能執行任務。為了判斷優先執行哪個線程,給每一個線程設置了一個優先級,優先級高的線程將會被優先執行。默認情況下,每個線程的優先級繼承它的父線程(創建該線程的線程)的優先級。優先級是1-10之間的整數,一般情況下,線程默認的優先級是5。常用的優先級有三個(1 : MIN_PRIORITY、 5 : NORM_PRIORITY、 10 : MAX_PRIORITY)。
public class Priority {          @Test      public void defaultPriority(){          int mainThreadPriority = Thread.currentThread().getPriority();          System.out.println("default priority is "+mainThreadPriority);//5      }          @Test      public void extendFather(){          Thread mainThread = Thread.currentThread();          mainThread.setPriority(4);          int mainThreadPriority = mainThread.getPriority();          System.out.println("main thread's priority is "+mainThreadPriority);//4          Thread t1 = new Thread(() -> System.out.println(Thread.currentThread().getName()),"t1");          System.out.println("t1 thread's priority is "+t1.getPriority());//4      }  }

相關API:

void setPriority(int priority)    設置線程優先級  int getPriority()             獲取線程優先級

 

守護線程

    守護線程的唯一作用就是為其它線程提供服務,永遠不要在守護線程中訪問固有資源,比如文件、數據庫等。當所有的用戶線程都結束後,守護線程才會結束。且當只剩下守護線程,虛擬機就會退出。
 
    默認情況下,由用戶線程創建的線程仍是用戶線程,由守護線程創建的線程仍是守護線程。
 
    Java虛擬機的垃圾回收線程就是典型的守護線程。
 
    相關API:
void setDaemon(boolean isdaemon) 設置線程為守護線程或者用戶線程。該方法必須在線程啟動之前調用。  boolean isDaemon()          判斷該線程是否為後台線程

 

線程組

    線程組是一個可以統一管理的線程集合,線程組也可以包含其它線程組。默認情況下,所有的線程屬於同一個線程組。線程只能訪問自己所在線程組的信息,不能訪問其它線程組的信息,包括該線程組的父線程組。
 
    建議不要使用線程組(已經有更好的特性用於線程集合的操作)
public class ThreadGroupDemo {        public static void print(){          Thread thread = Thread.currentThread();          System.out.println(thread.getThreadGroup().getName()+"-"+thread.getName());      }        public static void main(String[] args) {          ThreadGroup group = new ThreadGroup("Print Group");          new Thread(group, ThreadGroupDemo::print, "t1").start();          new Thread(group, ThreadGroupDemo::print, "t2").start();          group.list();        }  }

 

未捕獲異常處理器

  線程的run()不能拋出任何被檢測的異常【因為Runnable接口中定義的run()沒有拋出異常,所以重寫run()時,不允許拋出異常,可以使用try-catch捕獲異常】。但是,如果不被檢測的異常沒有使用try-catch處理,發生異常時會導致線程死亡。(比如下面這個例子,控制台並沒有輸出“Endind?”)

public class UncaughtExceptionDemo implements Runnable{        @Override      public void run() {          int i = 1/0;          System.out.println("Endind?");      }          public static void main(String[] args) {          ExecutorService service = Executors.newFixedThreadPool(1);          service.execute(new UncaughtExceptionDemo());      }  }    /* log:  ...  Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero      at concurrency.attributes.UncaughtExceptionDemo.run(UncaughtExceptionDemo.java:10)      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)      at java.lang.Thread.run(Thread.java:748)  */

  而且,在run中手動拋出了一個運行時異常,在main中使用try-catch處理異常,並未生效。(如下例,控制台並未輸出”Current thread occurs exception.”)

public class UncaughtExceptionDemo implements Runnable{        @Override      public void run() {          int i = 1/0;      }          public static void main(String[] args) {          try{              ExecutorService service = Executors.newFixedThreadPool(1);              service.execute(new UncaughtExceptionDemo());          } catch (RuntimeException e){              System.out.println("Current thread occurs exception.");          }      }  }    /*  ...  Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero      at concurrency.attributes.UncaughtExceptionDemo.run(UncaughtExceptionDemo.java:10)      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)      at java.lang.Thread.run(Thread.java:748)  */

   那麼,應該如何處理線程中拋出的異常呢?事實上,異常發生後,在線程死亡之前,異常會被傳遞到一個用於未捕獲異常的處理器。可以使用該處理器來處理線程中拋出的異常。

import java.util.concurrent.ExecutorService;  import java.util.concurrent.Executors;      public class ThreadExceptionResolve implements Runnable{          @Override      public void run() {          int i = 1/0;          System.out.println("Endind?");      }          public static void main(String[] args) {          //2. 為所有的線程設置“異常處理器”          Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());          //3. 創建並執行線程          ExecutorService service = Executors.newFixedThreadPool(1);          service.execute(new ThreadExceptionResolve());      }  }      //1. 定義符合線程異常處理器規範的“異常處理器”  // 該處理器必須屬於一個實現Thread.UncaughtExceptionHandler接口的類  class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {          @Override      public void uncaughtException(Thread t, Throwable e) {          System.out.println("The acculation is error.");      }  }    /*  ...  The acculation is error.  */

  安裝處理器的方式有兩種:可以使用Thread的靜態方法setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)為所有線程設置默認處理器,也可以使用Thread的實例方法setUncaughtExceptionHandler(UncaughtExceptionHandler eh)為摸一個線程設定處理器。如果線程沒有安裝處理器,此時的處理器就是該線程的ThreadGroup對象。

//為所有線程設置默認處理器  Thread.setDefaultUncaughtExceptionHandler(handler);      //為指定線程設置處理器  Thread mainThread = Thread.currentThread();  mainThread.setUncaughtExceptionHandler(handler);