1. 程式人生 > >多線程三(線程組和線程池)

多線程三(線程組和線程池)

@override executors cpu eight death java 7 中斷 屬於 空閑

線程組和線程池

一. 線程組

1. 線程組介紹及使用

Java使用ThreadGroup來表示線程組,它可以對一批線程進行分類管理,Java允許直接對線程組進行控制。對線程組的控制相當於控制這批線程。

在默認情況下,子線程和創建它的父線程同屬於一個線程組。

一旦線程假如某個線程組之後,該線程將一直屬於該線程組,知道該線程死亡,線程運行途中不能改變它所屬的線程組。

Thread提供了不同構造器設置新創建的線程屬於哪個線程組。提供getThreadGroup()方法返回該線程所屬的線程組對象。

ThreadGroup類提供了如下兩個構造器創建實例。

  • ThreadGroup(String name):以指定的線程組名字來創建新的線程組
  • ThreadGroup(ThreadGroup parent,String name):以指定的名字、指定的父線程組創建一個新線程組

Java程序不允許改線程組名字,通過getName()方法獲取線程組名字。

ThreadGroup類提供了如下常用的方法

  • int activeCount():返回此線程組中活動的線程數目
  • interrupt():中斷此線程組中的所有線程
  • isDaemon():判斷該線程組是否是後臺線程組
  • setDaemon(boolean daemon):把該線程組設置成後臺線程組。
  • setMaxPriority(int pri):設置線程組的最高優先級。

2.線程組和異常處理機制

從Java 5開始,Java加強了線程的異常處理,如果線程執行過程中拋出了一個未處理異常,JVM在結束該線程之前會自動查找是否有對應的Thread.UncaughtExceptionHandler對象,如果找到該處理器對象,則會調用該對象的uncaughtException(Thread t,Throwable e)方法來處理該異常。

Thread類提供了兩個方法設置異常處理器。

  • static setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):為該線程類的所有線程實例設置默認的異常處理器
  • setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh):為指定的線程實例設置異常處理器

ThreadGroup類實現了Thread.UncaughtExceptionHandler接口,所以每個線程所屬的線程組將會作為默認的異常處理器。

如果線程執行過程中拋出了一個未處理異常,JVM在結束該線程之前會自動查找是否有對應的Thread.UncaughtExceptionHandler對象,如果找到該處理器對象,則會調用該對象的uncaughtException(Thread t,Throwable e)方法來處理該異常;否則,JVM會調用該線程所屬的線程組對象的uncaughtException()方法來處理該異常。

線程組處理異常的流程如下:

  • 如果該線程組有父線程組,則調父線程組的uncaughtException()方法來處理該異常。
  • 如果該線線程實例所屬的線程類有默認的異常處理器,那麽調用該異常處理器來處理異常
  • 如果該對象是ThreadDeath對象,則不做任何處理;否則,將異常跟蹤棧的信息打印到System.err錯誤輸出流,並結束該線程。

下面主程序設置了異常處理器。

package com.gdut.thread;

class MyExHandler implements Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println(t+"線程出現了異常"+e);
    }
}
public class ExHandler {
    public static void main(String[] args) {
        Thread.currentThread().setUncaughtExceptionHandler(new MyExHandler());
        int a=5/0;
        System.out.println("程序正常結束");
    }
}

輸出:Thread[main,5,main]線程出現了異常java.lang.ArithmeticException: / by zero

二. 線程池

系統啟動一個新線程的成本是非常高的,因為它涉及與操作系統交互。當程序中需要創建大量生存期很短暫的線程時,應該考慮使用線程池來提高系統性能。

與數據庫連接池類似的是,線程池在系統啟動時即創建大量空閑的線程,當序將一個Runnable對象或Callable對象創給線程池,線程池就會啟動一個線程來執行他們的run()或call()方法,當run()或call()方法執行結束後,該線程並不會死亡,而是再次返回線程池中稱為空閑狀態,等待執行下一個Runnable對象的run()或call()方法。

除此之外,使用線程池可以有效地控制系統中並發線程的數量,當系統中包含大量並發線程時,會導致系統性能劇烈下降,甚至導致JVM崩潰。

2.1 Java 8改進的線程池

2.2 Java 8增強的線池

為了充分利用多CPU的優勢、多核CPU的性能優勢。可以考多個小任務,把小任務放到多個處理器核心上並行執行;當多個小任務執行完成之後,再將這些執行結果合並起來即可。Java 7提供了ForkJoinPool來支持這個功能。

ForkJoinPool是ExecutorService的實現類,因此是一種特殊的線程池。提供了如下兩個常用的構造器

  • ForkJoinPool(int parallelism):創建一個包含parallelism個並行線程的ForkJoinPool.
  • ForkJoinPool():以Runtime.availableProssesors()方法的返回值作為paralelism參數來創建ForkJoinPool.

Java 8進一步拓展了ForkJoinPool的功能,Java 8增加了通用池功能。ForkJoinPool通過如下兩個方法提供通用池功能。

  • ForkJoinPool commonPool():該方法返回一個通用池,通用池的狀態不會受shutdown()或shutdownNow()方法的影響。
  • int getCommonPoolParallelism():該方法返回通用池的並行級別。

創建了通用池ForkJoinPool實例之後,就可調用ForkJoinPool的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法來執行指定任務了。其中,ForkJoinTask代表一個並行,合並的任務。

ForkJoinTask是一個抽象類,它還有兩個抽象子類:RecursiveAction和recursiveTask。其中RecursiveAction代表沒有返回值的任務,RecursiveTask代表有返回值的任務。

下面程序將一個大任務(打印0~500)的數值分成多個小任務,並將任務交給ForkJoinPool來執行。

package com.gdut.thread.threadPool;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;

class PrintTask extends RecursiveAction{

    private static final int THRESHOLD = 50;
    private int start;
    private int end;
    public PrintTask(int start,int end) {
      this.start = start;
      this.end = end;
    }

    @Override
    protected void compute() {
        if(end-start<THRESHOLD){
            for (int i = start; i <end ; i++) {
                System.out.println(Thread.currentThread().getName()+"的i值"+i);
            }
        }else{
            //當end與start的差大於THRESHOLD時,即要打印的數超過50時,將大任務分成兩個小任務
            int middle = (end+start)/2;
            PrintTask left = new PrintTask(start,middle);
            PrintTask right = new PrintTask(middle,end);
            left.fork();
            right.fork();
        }
    }
}
public class ForkJoinPoolTest{
    public static void main(String[] args) throws InterruptedException{
        ForkJoinPool pool = new ForkJoinPool();
        pool.submit(new PrintTask(0,500));
        pool.awaitTermination(2, TimeUnit.SECONDS);
        pool.shutdown();
    }

}

技術分享圖片

8核計算機的執行效果

多線程三(線程組和線程池)