基本的線程機制

線程是比進程更輕量級的調度執行單位,各個線程既可以共享進程資源(內存地址、文件IO),又可以獨立調度(線程是CPU調度的最小單元)。

多進程和多線程的區別:
1、本質區別在於每個進程擁有自己一整套變量,線程則是共享數據。因此共享變量使線程之間的通信比進程之間的通信更有效、更容易。
2、與進程相比較,線程更“輕量級”,創建、撤銷一個線程比啓動新進程的開銷要小的多。

一、定義任務
1)通過實現Runnable接口並編寫run()方法,使得任務可以執行我們的命令。
Runnable執行工作的獨立任務,但不會返回任何值。

class LiftOff implements Runnable{
    protected int countDown = 10;
    private static int taskCount = 0;
    private final int id = taskCount++;
    @Override
    public void run() {
        while (countDown-- > 0){
            System.out.println(countDown);
            Thread.yield();
        }
    }
}

2)希望任務結束後有一個返回值,需要實現Callable接口並編寫call方法。Callable是一個具有類型參數的泛型,它的類型參數表示是從方法call中返回的值,並且必須使用ExecutorService.submit()方法調用它。

class TaskWithResult implements Callable<String>{
    private int id;
    public TaskWithResult(int id){
        this.id = id;
    }   
    @Override
    public String call() throws Exception {
        return "result of TaskWithResult " + id;
    }
}
public static void main(String[] args){
    ExecutorService exec = Executors.newCachedThreadPool();
    ArrayList<Future<String>> results = new ArrayList<>();
    for (int i = 0; i < 10; i++){
        results.add(exec.submit(new TaskWithResult(i)));
    }
    try {
        for (Future<String> future: results){
            future.get();
        }
    }catch (ExecutionException e){
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

submit()方法會產生Future對象,它用Callable返回結果的特定類型進行參數化,isDone()方法用來查詢Future是否已經完成,任務完成時,它具有一個結果,可以調用get()獲取該結果。也可以不用isDone()檢查直接調用get(),get()方法將阻塞,直至結果準備就緒。

二、線程的創建

1)Thread類來創建線程。Thread構造器只需一個Runnable對象。調用Thread對象的start()方法爲該線程執行必需的初始化操作,然後調用Runnable的run()方法,以便在這個新線程中啓動該任務
Thread t = new Thread(new LiftOff());
t.start();

start()調用後會快速的返回,run()方法會在新線程中執行
2)使用Executor創建線程。執行器(Executor)管理Thread對象,從而簡化併發編程。Executor在客戶端和任務執行之間提供了一個間接層;與客戶端直接執行任務不同,中介對象將執行任務。Executor允許你管理異步任務的執行,而無需顯式地管理線程的生命週期。
一般單個的Executor被用來創建和管理系統中所有的任務。shutDown()方法的調用可以防止新任務被提交給這個Executor。

FixedThreadPool可以一次性預先執行高昂的線程分配,限定線程的數量。可以節省時間,因爲不用爲每個線程都固定地付出創建線程的開銷。
CachedThreadPool通常會創建與所需數量相同的線程,然後在它回收舊線程時停止創建新線程。因此是首選,當這種方式會引發問題時,才需要切換到FixedThreadPool.
SingleThreadExecutor創建一個線程,每個任務都會在下一個任務開始之前運行結束,所有任務都使用相同的線程

三、線程常見名詞

3.1休眠 sleep()
調用sleep()將使任務終止執行給定的時間
對sleep()的調用可以拋出InterruptedException異常,異常不能誇線程傳播,所以必須在本地處理所有任務內部產生的異常

try {
    TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

3.2線程優先級
線程的優先級將線程的重要性傳遞給調度器,儘管CPU處理線程集的順序不確定,但是調度器將傾向於讓優先級高的線程先執行。並不是優先級底的線程得不到執行,僅僅是執行的評率較低。
獲取線程優先級Thread.currentThread().getPriority();
設置線程優先級Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
設置優先級實在run()方法的開頭設定的,在構造器中設定不會有任何好處,因爲Executor在此刻還沒有執行任務
JDK有10個優先級,因爲與操作系統不能很好的映射,只使用MAX_PRIORITY, MIN_PRIORITY,NORM_PRIORITY三種級別。

3.3讓步 yield()
通過調用yield()可以給線程調度器暗示,讓別的線程使用CPU。yield()的調用也是建議具有相同優先級的線程可以運行。對於任何重要的控制或在調整應用時,都不能依賴於yield()。

3.4後臺線程 daemon
後臺線程指在程序運行的時候在後臺提供一種通用服務的線程,並且這種線程不屬於程序中不可或缺的部分。所有非後臺線程結束時,程序也就終止了,同時會殺死所有後臺線程。
必須在線程啓動之前調用setDaemon()方法,才能把它設置爲後臺線程。
Thread t = new Thread(new LiftOff());
t.setDaemon(true);
t.start();

通過定製ThreadFaactory可以定製由Executor創建的線程的屬性(後臺、優先級、名稱)

class DaemonThreadFactory implements ThreadFactory{
    @Override
    public Thread newThread(@NonNull Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}
ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory());

可以通過isDaemon()來判斷一個線程是否爲後臺線程。如果一個線程爲後臺線程那麼它創建的任何線程都是後臺線程。
注意:當最後一個非後臺線程終止時,後臺線程會“突然”終止,finally子句也不會執行

3.5加入一個線程join()
一個線程可以在其他線程之上調用join()方法,效果爲等待一段時間直到第二個線程結束才繼續執行。如某個線程在另一個線程t上調用t.join(),此線程將被掛起,直到目標線程t結束才恢復。
也可以在join()方法中帶一個參數,如果目標線程在參數時間到期還沒有結束,join()方法總能返回。

3.6捕獲異常
由於線程的本質特性,不能捕獲從線程中逃逸的異常。一旦異常逃逸出任務的run()方法,就會向外傳播到控制檯,除非採取特殊的步驟捕獲異常。
Thread.UncaughtExceptionHandler允許在每個Thread對象上附着一個異常處理器

Thread t = new Thread(new LiftOff());
   t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
      @Override
       public void uncaughtException(Thread t, Throwable e) {      
         }
       });
       t.start();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章