利用Java反射機制,獲取ThreadPoolExecutor線程池中的workers線程隊列


應用場景:
將若干有唯一任務Id的線程放到ThreadPoolExecutor中執行,在此之前保存任務Id和線程的關系:Map<String, TestThread> taskIdCutThreadMap 在某些特殊情況下任務線程會異常崩潰而主進程無感知,此時taskIdCutThreadMap的size和ThreadPoolExecutor的poolsize數量就會對不上。

需求: 排查出哪些線程異常崩潰

解決方案:
1.線程設置定時心跳上報,當無心跳時則判斷線程死亡 2.為每個線程設置名為任務Id的線程名,然后定時獲取ThreadPoolExecutor線程池中線程集合,根據線程名排查出異常任務

方案一的實現比較簡單,本文探討方案二如何實現,即如何獲取ThreadPoolExecutor中運行的線程集合。

 

ThreadPoolExecutor中有一個私有集合對象workers,它是線程池中所有工作線程的集合。

集合存放的Worker類是定義在ThreadPoolExecutor中的內部類,源碼就不貼了。

Worker類主要維護了4個參數:

1,final Thread thread;

thread屬性是Worker維護的線程,每個Worker對象一個,這個就是用來執行任務用的線程,也就是說,Worker對象的數量也就代表了線程池中活動線程的數量。

2,Runnable firstTask;

Worker對象的初始任務,多數情況下每個Worker對象創建時都伴隨一個初始任務,是這個Worker對象優先會執行的任務,當執行完firstTask后,Worker對象還會從線程池中繼續獲取任務並執行。

3,volatile long completedTasks;

該Worker對象執行完成的任務數。

4,private static final long serialVersionUID = 6138294804551838833L;

序列化UID,沒什么用,注釋寫的也很坦誠,用不上,加這個參數就是為了不想看到Java的警告。

 

在本次需求中,我們要獲取的就是ThreadPoolExecutor中所有工作線程的集合workers以及集合里每個Worker類的thread屬性對象。

但是由於workers是被private修飾的,ThreadPoolExecutor也沒有提供獲取workers的公共方法,所以我們需要通過java的反射機制去主動獲取這個屬性值並完成解析。

以下是代碼:

/**
     * @throws Exception
     * @Description: 獲取ThreadPoolExecutor中私有HashSet集合對象workers的數據
     * @Param:
     * @return:
     * @author: hw
     * @date: 2020/12/15 16:54
     */
    public static void getThreadPoolWorkers(ThreadPoolExecutor cutExecutor) {
        //1.獲取私有對象workers
        Object resultValue = getPrivateClass(cutExecutor,"workers");
        HashSet workers = (HashSet) resultValue;
        //2.遍歷workers集合
        Iterator iterator = workers.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            //3.獲取workers的私有屬性thread對象
            Object workerValue = getPrivateClass(obj,"thread");
            Thread workerThread = (Thread) workerValue;
            System.out.println("線程名:" + workerThread.getName());
        }
    }

    /**
    * @Description: 獲取類的某個私有屬性
    * @Param:
    * @return:
    * @throws Exception
    * @author: hw
    * @date: 2020/12/15 17:11
    */
    public static <T> Object getPrivateClass(T t, String param) {
        Object workerValue = null;
        Class workerCla = t.getClass();
        Field[] workerFields = workerCla.getDeclaredFields();
        for (Field workerField : workerFields) {
            // 私有屬性必須設置訪問權限
            workerField.setAccessible(true);
            // 獲取私有對象的屬性名稱
            String workerName = workerField.getName();
            if (param.equals(workerName)) {
                try {
                    // 獲取私有對象的屬性
                    workerValue = workerField.get(t);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return workerValue;
    }

至此,通過獲取到的線程名和taskIdCutThreadMap比較即可判斷出哪些線程異常崩潰了。

 

參考資料:

1.Java的反射機制 調用私有方法私有屬性

2.獲取object對象中的屬性值

 

資料拓展:

1 什么是反射

    JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性。
    這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。

2 Java反射機制的功能

        1)在運行時判斷任意一個對象所屬的類;
        2)在運行時構造任意一個類的對象;
        3)在運行時判斷任意一個類所具有的成員變量和方法;
        4)在運行時調用任意一個對象的方法;
        5)生成動態代理。

反射機制原理圖

 

 

3 Class類

Java類均繼承了Object類,因此,可以利用在Object類中已經定義的getClass()方法,返回一個類型為Class的對象。
 

常用方法
getMethods()——獲取所有權限為public的方法
getMethod(String methodName,Class<?> …paramTypes)——根據方法名、各入參的類型,返回權限為public的指定方法
getDeclaredMethods()——獲取所有方法
getDeclaredMethod(String methodName,Class<?> …paramTypes) ——根據方法名、各入參的類型,返回指定方法

同樣,對於成員變量、內部類的訪問方法,也分為限定權限public與否。

區別:
    由此可見,在通過方法getMethods()依次獲取權限為public的方法時,將包含從超類中繼承的方法;而通過方法getDeclaredMethods()只是獲得在本類中定義的方法。
    此規則同樣適用於getFields()及getDeclaredFields()。

 

4 反射訪問成員變量

在通過方法訪問成員變量時,將返回Field類型的對象或數組。每個Field對象代表一個成員變量,利用Field對象可以操縱相應的成員變量。
方法:
    getFields()
    getField(String name)
    getDeclaredFields()
    getDeclaredField(String name)

常用方法
    getName()——獲得該成員變量的名稱
    getType() ——獲得表示該成員變量類型的Class對象
    get(Object obj) ——獲得指定對象obj中成員變量的值,返回值為Object型
    set(Object obj,Object value) ——將指定對象obj中的成員變成的值設置為value
    getInt(Object obj) ——獲得指定對象obj中類型為int的成員變量的值
    setInt(Object obj,int i) ——將指定對象obj中類型為int的成員變量的值設置為i
    getFloat(Object obj) ——獲得指定對象obj中類型為float的成員變量的值
    setFloat(Object obj,float f) ——將指定對象obj中類型為float的成員變量的值設置為f
    getBoolean(Object obj) ——獲得指定對象obj中類型為boolean的成員變量的值
    setBoolean(Object obj,boolean z) ——將獲得指定對象obj中類型為boolean的成員變量的值設置為z
    setAccessible(Boolean flag) ——可以設置忽略權限限制直接訪問private等私有權限的成員變量
    getModifiers() ——獲得可以解析出該成員變量所采用的的修飾符的整數

set(Object obj,Object value)、setInt(Object obj,int i)、setFloat(Object obj,float f) 、setBoolean(Object obj,boolean z)僅能對權限為public、protected的成員變量進行操作。
    此時,需要利用setAccessible(Boolean flag)方法,設置為setAccessible(true),此時便可對private權限的成員變量進行修改。   

 

5 反射訪問方法

在通過反射機制訪問方法時,將返回Method類型的對象或數組。每個Method對象代表一個成員變量,利用Method對象可以操縱相應的成員變量。
方法:
    getMethods()
    getMethod (String name,Class<?>…paramTypes)
    getDeclaredMethods()
    getDeclaredMethod (String name,Class<?>…paramTypes)

如果是訪問指定的方法,需要根據該方法的名稱和入參的類型來訪問。
    例如,訪問一個名稱為test、入參類型依次為int、String、boolean的方法,可以通過以下兩種方式實現:
    1)objClass.getDeclaredMethod(“test”,int.class,String.class,boolean.class);
    2)objClass.getDeclaredMethod(“test”,new Class[]{int.class,String.class,boolean.class});

常用方法
    getName()——獲得該方法的名稱
    getParameterType() ——按照申明順序,以Class數組的形式獲得各個參數類型
    getReturnType() ——以Class對象的形式獲得該方法的返回值
    getExceptionTypes()——以Class數組的形式獲得該方法可能拋出的異常類型(包括try…catch、throws)
    invoke(Object obj,Object…args)——利用指定參數args,執行指定對象obj中的方法,返回值為Object類型
    isVarArgs()——查看該方法是否允許帶有可變數量的參數,允許返回true
    getModifiers() ——獲得可以解析出該成員變量所采用的的修飾符的整數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM