浅析ThreadList的runcheckpoint方法

2022-05-06 15:33:01 浏览数 (1)

作用

threadlist的runcheckpoint方法作用是让所有线程都执行制定的任务。threadlist的dump方法有两种实现方式:

1。打印调用者线程的栈帧情况

2。打印指定thread线程的栈帧情况。

根据上面两个函数相信大家可以猜到可以做哪些骚操作了。接下来继续解释

函数实现

runcheckpoint函数实现:

1。针对已经在运行的线程只需对这个线程设置kcheckpoint标志位,运行中的线程检查到kcheckpoint会自动打印当前线程的信息。而针对不在运行的线程需要先设置suspendrequest标志位让他们暂停运行,防止之后暂停的线程状态变为运行。所以针对已经运行的线程来说只要设置标志位即可,thread运行的时候会在特定指令处进行checkpoint方法进行检查标志位而作出对应的处理。

标记位种类:

1.kcheckpoint的标志位是取出自己tls区域的closure并执行指定run函数最多三个:通过tls区域保存 需要执行的run函数。run函数通过closure闭包封装。一个线程做多可以执行三个closure,也就是可以执行三个run方法。可通过add的方式进行添加对应线程tls区域的closure。

2.suspendrequest标志位是将线程状态设置为suspend暂停不运行。

解释:

threadlist中存放着所有的线程也知道所有运行的线程数是多少

将所有线程都暂停怎么验证呢?

假设当前threadlist中有5个运行的线程数量通过suspendbarrier保存为5,我给所有的线程都设置ksuspendrequest标志位,当它们碰到某些指令时比如循环返回,异常指令处方法返回处等指令处时会运行runcheckpoint检查自己的标志位。而suspendrequest对应的第一步操作就是递减suspendbarrir然后挂起自己修改自己线程状态,当递减为0时代表所有线程都暂停了。这个时候就是所有线程都暂停。递减操作是无锁编程通过循环递减barrry,通过cas原子操作保证线程安全

如果要是不在运行的线程呢?就是说这个线程阻塞了或者已经暂停了他都没有运行那么怎么进行检查标志位呢这个也就是上面说过的dump的第二种实现方式,不需要那个指定的线程运行自己打印自己线程的情况,而是通过调用者线程(你这个线程不运行,调用者线程运行我用调用者线程去打印)进行帮助那些没有运行的线程打印信息,只需要指定对应的thread。

上面第一步设置ksuspendrequest标志位已经让不在运行的线程都已经停止运行了(即使暂停的线程恢复运行也会在检查标志位的时候进行把自己暂停)然后我把一个执行dump函数的run函数封装成closure添加到threadlist中的每一个线程中。而dump函数是打印指定线程的栈帧信息。这里runcheckpoint都是通过指定线程的方式来打印对应指定线程的信息,执行线程是当前线程,但是打印对应线程情况的是指定的线程

为什么呢?因为要保存线程不能发生任何内存上的变化所以只能通过将所有线程都suspend的方式实现,但是线程暂停了怎么能够打印自己的信息呢?所以得留一个活着的线程也就是当前线程去打印他们的信息

总结

检查标志位的操作之后就会检查到自己线程被设置了kcheckpoint标志位从tsl区域中取出closure执行任务;设置失败代表当前线程不在运行状态,那么也不可以直接打印那个线程的信息,因为之后这个线程可能再次运行会改变线程的内存情况,所以针对暂停的线程还需要设置ksuspendpoint保证即使线程之后运行也会再次暂停(递减barrier成功才会返回true),这样就能保证当前是发生情况的现场对应的情况。

思考环节

大家仔细想想是不是我们anr的时候查看trace文件会出现一堆线程的信息不管是native的还是java的都会出现。答案就是通过threadlist的runcheckpoint实现的。

运行的线程保证及时的打印当时虚拟机中所有线程情况,不在运行的线程设置挂起标志位保证即使运行也能保证不会破坏现场。因为线程状态切换时也会进行一次标志位检查所以可以保证是之前那次发出指令时的内存情况。

0 人点赞