執行緒管理

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