執行緒管理

eacape發表於2021-04-03

1.執行緒組

​ 類似於計算機中,使用資料夾管理檔案,也可以使用執行緒組來管理執行緒,線上程組中定義一組相似的執行緒,在線上程組中也可以定義子執行緒組。

​ Thread類有幾個構造方法允許在建立執行緒時指定執行緒組,如果在建立執行緒時沒有指定執行緒組,則該執行緒就屬於父執行緒所在的執行緒組,JVM在建立main執行緒時會為它指定一個執行緒組,因此每個java執行緒都有一個執行緒組與之相關,可以呼叫getThreadGroup()返回執行緒組嗎。

1.1返回當前main的執行緒組

public class ThreadGroupText {
    public static void main(String[] args) {
        ThreadGroup threadGroup=Thread.currentThread().getThreadGroup();
        System.out.println(threadGroup);
    }
}

image-20210330224221571

1.2 定義執行緒組,如果不指定執行緒組,則自動歸為當前所屬的執行緒

public class ThreadGroupText {
    public static void main(String[] args) {
       ThreadGroup threadGroup1=new ThreadGroup("group1");
        System.out.println(threadGroup1);
    }
}

image-20210330225034049

1.3 定義執行緒組同時指定父執行緒

public class ThreadGroupText {
    public static void main(String[] args) {
        ThreadGroup threadGroup=Thread.currentThread().getThreadGroup();
        System.out.println(threadGroup);
        
         ThreadGroup threadGroup1=new ThreadGroup("group1");
        System.out.println(threadGroup1.getParent());
        
        ThreadGroup threadGroup2=new ThreadGroup(threadGroup,"group2");
        System.out.println("threadGroup1-->"+threadGroup1.getParent());
        System.out.println("threadGroup2-->"+threadGroup2.getParent());
    }
}

image-20210330225150689

1.4建立執行緒時指定所屬執行緒組

public class ThreadGroupText {
    public static void main(String[] args) {
        Runnable r=new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread());
            }
        };
        Thread t1=new Thread(r,"t1");
        System.out.println(t1);
    }
}
Thread[t1,5,main]//執行緒名稱 優先順序 父執行緒組

在main執行緒中建立了t1執行緒,main為父執行緒,t1為子執行緒,t1沒有指定執行緒組t1就屬於父執行緒組。

1.5執行緒組的基本操作

activecount()返回當前執行緒組及子執行緒組中活動執行緒的數量

activeGroupCount()返回當前執行緒組以及子執行緒組中活動執行緒組的數量

int enumerate(Thread[] list)將當前執行緒組中的活動執行緒複製到引數陣列中

enmerate(ThreadGroup[] list) 將當前執行緒組中的活動執行緒複製到引數陣列中

getMaxPriority()獲取執行緒組最大優先順序,預設是10

getParent()返回父執行緒組

getName()返回執行緒組的名字

interrup()中斷執行緒組的所有執行緒

IsDaemon()判斷當前執行緒組是否為守護執行緒

list()將當前執行緒組中的活動執行緒列印出來

ParentOf(HtreadGroup g)判斷當前執行緒組是否為引數執行緒組的父執行緒組

setDaemon()設定執行緒組為守護執行緒

2.捕獲執行緒的執行異常

​ 線上程Run方法中,如果有受檢異常必須捕獲處理,如果想要獲得Run方法中出現的執行時異常,可以通過回撥UncaughtExceptHandler介面獲得哪個執行緒出現了執行時異常。

2.1.Thread類相關異常處理方法

getdefaultUncaughtExceptHandle獲得全域性的UncaughtExceptHandler

getUncaughtExceptHandler獲得當前執行緒的UncaughtExceptHandler

setdefaultUncaughtExceptHandle設定全域性的UncaughtExceptHandler

setUncaughtExceptHandler設定當前執行緒的UncaughtExceptHandler

當執行緒出現異常,JVM會呼叫Thread類的dispatchcaughtExceptHandler(Throwable e)方法,該方法會呼叫

getUncaughtExceptHandler().UncaughtException(this e),如果想要獲得異常資訊,就需要設定執行緒的UncaughtExceptHandler

2.2設定執行緒異常的回撥介面方法

package com;

public class ThreadExcept{
    public static void main(String[] args) {
        //設定執行緒全域性回撥介面
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                //t引數接受發生異常的執行緒,e就是該執行緒中的異常資訊
                System.out.println(t.getName()+"發生了"+e.getMessage());
            }
        });

        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
           String name=null;
                System.out.println(name.length());
            }
        });
        t1.start();
    }
}

image-20210403152038162

​ 在實際開發中,這種設計異常處理方式還是比較常用的,尤其是異常執行方法

​ 如果執行緒產生異常,JVM會呼叫dispatchcaughtException方法,該方法中呼叫了getUncaughtExceptionHandler().UncaughtException(this e);如果當前執行緒設定了UncaughtExceptionHandler回撥介面就直接呼叫它自己的UncaughtException方法,如果沒有設定則呼叫當前執行緒所線上程組UncaughtExceptionHandler回撥介面UncaughtException方法,如果執行緒組也沒有設定回撥介面,則直接把異常的棧資訊定向Sysytem.err中。

3.注入Hook鉤子執行緒

​ 很多軟體包括Mysql、Zookeeper、Kafka都存在Hook執行緒的效驗機制,目的就是效驗程式是否已啟動,防止反覆啟動應用程式。

​ Hook執行緒也叫鉤子執行緒,當JVM退出的時候會執行Hook執行緒,經常在程式啟動的時候建立一個.lock執行緒,用.lock校驗程式是否在啟動,在程式退出時刪除.lock檔案,在Hook執行緒中處理防止重複啟動之外還可以做資源釋放,儘量避免在Hook執行緒中做複雜操作。

package com;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class HookText {
    public static void main(String[] args) throws IOException, InterruptedException {
        //注入Hook執行緒,在程式退出時,刪除.lock檔案
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                System.out.println("JVM退出,會啟動當前Hook執行緒,在Hook執行緒中刪除.lock檔案");
                getFile().toFile().delete();
            }
        });

        //檢查lock檔案是否存在
        if(getFile().toFile().exists())
        {
            throw  new RuntimeException("程式已啟動");
        }
        else
        {
            getFile().toFile().createNewFile();
            System.out.println("建立lock檔案");
        }

        for (int i = 0; i < 100; i++) {
            System.out.println("程式正在執行");
            Thread.sleep(100);
        }
    }

    private  static Path getFile()
    {
        return Paths.get("","tmp.lock");
    }
}

當專案已啟動,JVM會在專案資料夾目錄會自動建立一個.lock檔案

image-20210403154703343

只有當專案自動執行結束JVM自動退出時會刪除.lock檔案,當讓程式執行時停止,.lock檔案不會被刪除,再執行會丟擲異常

image-20210403155048752

相關文章