利用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反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。
这种动态获取信息以及动态调用对象方法的功能称为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() ——获得可以解析出该成员变量所采用的的修饰符的整数