做网站推广的难点、,网站首页做30个关键词,网站建设需要上传数据库吗,app设计网站模板转载自 Java中如何实现线程的超时中断
背景
之前在实现熔断降级组件的时候#xff0c;需要实现接口请求的超时中断。意思是#xff0c;业务在使用熔断降级功能时#xff0c;在平台上设置了一个超时时间#xff0c;如果请求进入熔断器开始计时#xff0c;接口在超时时间…转载自 Java中如何实现线程的超时中断
背景
之前在实现熔断降级组件的时候需要实现接口请求的超时中断。意思是业务在使用熔断降级功能时在平台上设置了一个超时时间如果请求进入熔断器开始计时接口在超时时间内没有响应则需要提早中断该请求并返回。
比如正常下游接口的超时时间为800ms但是因为自身业务的特殊需求最多只能等200ms如果指定之内没有数据返回则返回降级数据。这里处理请求的线程可以看成是tomcat线程池中的一个线程如果通过线程池返回的Future可以很轻松的实现超时中断返回但是当前情况下并不能拿到Futrue又不想额外引入一个线程池所以需要另外一种实现思路。
思路
中断一个线程的思路有哪些 除了已经废弃的Thread.stop, Thread.suspend, Thread.resume 方法剩下的貌似只有一种方案了就是调用当前线程的 interrupt()但是这个方法的作用并不是中断线程而是设置一个标识通知该线程可以被中断了到底是继续执行还是中断返回由线程本身自己决定。
具体来说当对一个线程调用了 interrupt()之后如果该线程处于被阻塞状态比如执行了wait、sleep或join等方法那么会立即退出阻塞状态并抛出一个 InterruptedException异常在代码中catch这个异常进行后续处理。如果线程一直处于运行状态那么只会把该线程的中断标志设置为 true仅此而已所以 interrupt()并不能真正的中断线程不过在rpc调用的场景中请求线程一般都处于阻塞状态等待数据返回这时 interrupt()方法是可以派上用场的。
那么要实现指定超时时间内中断请求线程还有最后一个问题需要解决什么时候由谁去执行 interrupt()方法
必然这个方法只能由其它线程来执行了自己都阻塞了执行个鬼而且是在请求进入熔断器时并在超时时间之后执行有点绕比如超时时间是200ms那么请求进入熔断器之后再过200ms就执行 interrupt()但是在200ms之内有数据返回那么就不执行 interrupt()了。
实现
需求已经很明确了相当于延迟执行一个task其内部逻辑就是执行请求线程的 interrupt()当然还有其它的逻辑。
Runnable task new Runnable() {Overridepublic void run() {try {thread.interrupt();// 取消定时器任务f.cancel();} catch (Exception e) {logger.error(Failed while ticking TimerListener, e);}}
};
Doug Lea大神提供的 ScheduledThreadPoolExecutor可以很好的满足这个需求通过 scheduleAtFixedRate方法可以很方便的实现在延迟指定时间之后执行提交的任务。 ScheduledFuture? f executor.scheduleAtFixedRate(task, timeout, timeout, TimeUnit.MILLISECONDS);
在请求进入熔断器时顺便提交一个任务到线程池中等待执行如果接口在超时时间内没有返回那么该任务会被触发并执行请求线程的 interrupt方法这样就实现了请求线程的中断因为这时请求线程正在被阻塞等待数据返回另外需要清空定时任务不然这个任务会一直执行。
如果接口正常返回了也要记得清空定时任务并且在请求退出熔断器的时候记得恢复请求线程的中断标识如何恢复在请求线程中执行下面代码即可。
Thread.interrupted();// 内部逻辑
public static boolean interrupted() {return currentThread().isInterrupted(true);
}// 参数为true可以清除中断标识
private native boolean isInterrupted(boolean ClearInterrupted);
执行当前线程即请求线程的isInterrupted方法。
使用这种方式实现请求的超时中断在QPS很高的情况下会有额外的性能损失因为每次请求都要提交一个任务到线程池中等待执行。