阻塞检查机制
如何感知线程是否发生阻塞,在很多应用场景下这都是值得我们去思考的问题。如果线程因为各种原因长时间发生阻塞,则会影响系统执行效率,降低系统tps。
为了感知线程阻塞问题,我们可以建立一套线程阻塞自感知机制,对线程状态进行监控,当检测到当前任务执行线程的任务执行时间过长时,自动触发告警,提醒开发人员系统存在线程阻塞,进行问题定位和排查。
如何实现线程阻塞检查机制
1. 设计思路
设置一个block checker,业务线程执行任务前,在block checker处注册阻塞监控,设置阻塞时间阈值,业务线程执行完任务后,通知block checker任务完成,监控解除。
若超过阻塞时间阈值但block checker仍未收到业务线程的任务完成通知,则触发阻塞告警。
2. 实现方案
将block checker设置为定时任务线程池。业务线程执行任务前,往block checker中添加delay time为阻塞时间阈值的定时任务,同时传入回调方法作为阻塞触发时的action。
业务线程执行完任务后,需解除定时任务,否则到达阻塞时间阈值后会触发阻塞回调。
3. 实现代码
/**
* 阻塞监控触发future对象
*
* @author ian
* @date 2022年9月1日 下午5:50:51
*/
public class BlockCheckFuture {
private final ScheduledFuture<?> scheduledFuture;
public BlockCheckFuture(ScheduledFuture<?> scheduledFuture) {
this.scheduledFuture = scheduledFuture;
}
/**
* 结束阻塞监控
*/
public void endCheck() {
if (!scheduledFuture.isCancelled()) {
scheduledFuture.cancel(false);
}
}
}
/**
* 线程阻塞检查器
*
* @author ian
* @date 2022年9月1日 下午5:46:38
*/
public class BlockCheckerUtil {
private static final ScheduledExecutorService scheduledExecutorService;
private static final int CORE_SIZE = 1;
static {
scheduledExecutorService = new ScheduledThreadPoolExecutor(CORE_SIZE, r -> {
Thread t = new Thread(r);
// 设置为daemon,低优先级,减少cpu占用
t.setDaemon(true);
t.setName("blockCheckerThread");
return t;
});
}
/**
* 阻塞监控
*
* @param time 时间
* @param timeUnit 时间单位
* @param thresholdCallback 阻塞回调
* @return block check future,for ending checking monitor
*/
public static BlockCheckFuture blockCheck(long time, TimeUnit timeUnit, ThresholdHitCallback thresholdCallback) {
Thread current = Thread.currentThread();
ScheduledFuture<?> future = scheduledExecutorService.schedule(
() -> thresholdCallback.callback(current), time, timeUnit);
return new BlockCheckFuture(future);
}
}
4. 使用示例
public static void main(String[] args) {
BlockCheckFuture future = null;
try{
// 阻塞感知
future = BlockCheckerUtil.blockCheck(2000, TimeUnit.MILLISECONDS, new ThresholdHitCallback() {
@Override
public void callback(Thread observedThread) {
Log.error("thread is blocking !");
}
});
// do your business ...
} finally {
future.endCheck();
}
}