1. 程式人生 > >併發程式設計之執行緒屬性

併發程式設計之執行緒屬性

  本文主要討論執行緒的各種屬性,包括:主執行緒、執行緒優先順序、守護執行緒、執行緒組、處理未捕獲異常的處理器。

  主執行緒     任何一個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);