【java读书笔记】ThreadGroup和钩子线程的使用
文章目錄
- ThreadGroup介紹
- ThreadGroup的基本操作
- ThreadGroup的其他方法
- 鉤子線程
- 用hook線程模擬防重復啟動程序
ThreadGroup介紹
ThreadGroup并不能提供對線程的管理,ThreadGroup的主要功能是對線程進行組織,下面將詳細介紹ThreadGroup的主要方法。
ThreadGroup的基本操作
activeCount()用于獲取group中活躍的線程,這只是個估計值,并不能百分之百地保證數字一定正確,原因前面已經分析過,該方法會遞歸獲取其他子group中的活躍線程。
activeGroupCount()用于獲取group中活躍的子group,這也是一個近似估值,該方法也會遞歸獲取所有的子group。
getMaxPriority()用于獲取group的優先級,默認情況下,Group的優先級為10,在該group中,所有線程的優先級都不能大于group的優先級。
getName()用于獲取group的名字。
getParent()用于獲取group的父group,如果父group不存在,則會返回null,比如system group的父group就為null。 list()該方法沒有返回值,執行該方法會將group中所有的活躍線程信息全部輸出到控制臺,也就是System.out。
parentOf(ThreadGroup g)會判斷當前group是不是給定group的父group,另外如果給定的group就是自己本身,那么該方法也會返回true。
setMaxPriority(int pri)會指定group的最大優先級,最大優先級不能超過父group的最大優先級,執行該方法不僅會改變當前group的最大優先級,還會改變所有子group的最大優先級。 下面我們給出一個簡單的例子來測試一下上面的幾個方法。
package com.wangwenjun.concurrent.chapter06;import java.util.concurrent.TimeUnit;public class ThreadGroupBasic {public static void main(String[] args) throws InterruptedException{/** Create a thread group and thread.*/ThreadGroup group = new ThreadGroup("group1");Thread thread = new Thread(group, () ->{while (true){try{TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e){e.printStackTrace();}}}, "thread");thread.setDaemon(true);thread.start();//make sure the thread is startedTimeUnit.MILLISECONDS.sleep(1);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();System.out.println("activeCount=" + mainGroup.activeCount());System.out.println("activeGroupCount=" + mainGroup.activeGroupCount());System.out.println("getMaxPriority=" + mainGroup.getMaxPriority());System.out.println("getName=" + mainGroup.getName());System.out.println("getParent=" + mainGroup.getParent());mainGroup.list();System.out.println("--------------------------");System.out.println("parentOf="+mainGroup.parentOf(group));System.out.println("parentOf="+mainGroup.parentOf(mainGroup));}}這里需要特別說明的是setMaxPriority,我們通過分析源碼得出結論,線程的最大優先級,不能高于所在線程組的最大優先級,但是如果我們把代碼寫成下面這樣會怎么樣呢?
ThreadGroup的其他方法
ThreadGroup的interrupt
interrupt一個thread group會導致該group中所有的active線程都被interrupt,也就是說該group中每一個線程的interrupt標識都被設置了
ThreadGroup的destroy
destroy用于銷毀ThreadGroup,該方法只是針對一個沒有任何active線程的group進行一次destroy標記,調用該方法的直接結果是在父group中將自己移除:
鉤子線程
JVM進程的退出是由于JVM進程中沒有活躍的非守護線程,或者收到了系統中斷信號,向JVM程序注入一個Hook線程,在JVM進程退出的時候,Hook線程會啟動執行,通過Runtime可以為JVM注入多個Hook線程,下面就通過一個簡單的例子來看一下如何向Java程序注入Hook線程。
ThreadHook.java package com.wangwenjun.concurrent.chapter07;import java.util.concurrent.TimeUnit;public class ThreadHook {public static void main(String[] args){//為應用程序注入鉤子線程Runtime.getRuntime().addShutdownHook(new Thread(){@Overridepublic void run(){try{System.out.println("The hook thread 1 is running.");TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e){e.printStackTrace();}System.out.println("The hook thread 1 will exit.");}});//鉤子線程可注冊多個Runtime.getRuntime().addShutdownHook(new Thread(){@Overridepublic void run(){try{System.out.println("The hook thread 2 is running.");TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e){e.printStackTrace();}System.out.println("The hook thread 2 will exit.");}});System.out.println("The program will is stopping.");} }在上面代碼中,給Java程序注入了兩個Hook線程,在main線程中結束,也就是JVM中沒有了活動的非守護線程,JVM進程即將退出時,兩個Hook線程會被啟動并且運行,輸出結果如下:
在我們的開發中經常會遇到Hook線程,比如為了防止某個程序被重復啟動,在進程啟動時會創建一個lock文件,進程收到中斷信號的時候會刪除這個lock文件,我們在mysql服務器、zookeeper、kafka等系統中都能看到lock文件的存在,本節中,將利用hook線程的特點,模擬一個防止重復啟動的程序,
用hook線程模擬防重復啟動程序
package com.wangwenjun.concurrent.chapter07;import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; import java.util.Set; import java.util.concurrent.TimeUnit;public class PreventDuplicated {private final static String LOCK_PATH = "/home/wangwenjun/locks/";private final static String LOCK_FILE = ".lock";private final static String PERMISSIONS = "rw-------";public static void main(String[] args) throws IOException {//① 注入Hook線程,在程序退出時刪除lock文件Runtime.getRuntime().addShutdownHook(new Thread(() ->{System.out.println("The program received kill SIGNAL.");getLockFile().toFile().delete();}));//② 檢查是否存在.lock文件checkRunning();//③ 簡單模擬當前程序正在運行for (; ; ){try{TimeUnit.MILLISECONDS.sleep(1);System.out.println("program is running.");} catch (InterruptedException e){e.printStackTrace();}}}private static void checkRunning() throws IOException{Path path = getLockFile();if (path.toFile().exists())throw new RuntimeException("The program already running.");Set<PosixFilePermission> perms = PosixFilePermissions.fromString(PERMISS-IONS);Files.createFile(path, PosixFilePermissions.asFileAttribute(perms));}private static Path getLockFile(){return Paths.get(LOCK_PATH, LOCK_FILE);} }運行上面的程序,會發現在/home/wangwenjun/locks目錄下多了一個.lock文件,程序運行則會創建.lock文件以防止重復啟動
執行kill pid或者kill-1 pid命令之后,JVM進程會收到中斷信號,并且啟動hook線程刪除.lock文件。
Hook線程是一個非常好的機制,可以幫助程序獲得進程中斷信號,有機會在進程退出之前做一些資源釋放的動作,或者做一些告警通知。切記如果強制殺死進程,那么進程將不會收到任何中斷信號。
注:筆記來自書籍《java高并發編程詳解》
總結
以上是生活随笔為你收集整理的【java读书笔记】ThreadGroup和钩子线程的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: shiro+jwt进行认证和授权的解决方
- 下一篇: 解决配置shiro+jwt后swagge