中断线程池中运行时间长的线程
在我们实际开发中经常使用线程池来进行异步操作,但可能因为某些原因导致线程池中单个线程发生阻塞,导致程序运行效率下降,因此我们很自然的想到,如何暂停线程池中长时间阻塞的线程。接下来我将给出我的方案。
方案
我们为每个任务赋予可以容忍的最大运行时间,当任务在提交给线程池中时,这些任务会被添加到自定义的延迟队列中,延迟队列中他们都是按最大运行时间进行排序,并开启了一个单独的线程不断去获取延迟队列中的元素,如果获取到了任务,表示该任务超时,我们就可以中断它。其次那些并未超时的任务,会在任务执行完成之后从延迟队列中删除。
延迟任务创建
/**
* @date 2022/3/24
* @author zwd
* @email zwd@hhh.com
*/
public class DelayTask implements Delayed{
private String name ;
private long time;
private long start = System.currentTimeMillis();
DelayTask(String name, long time) {
this.name = name;
this.time = time;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert((start+time) - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return "DelayTask{" +
"name='" + name + ''' +
", time=" + time +
'}';
}
}
/**
* @date 2022/3/24
* @author zwd
* @email zwd@hhh.com
*/
public class DelayTaskThread extends DelayTask implements Runnable{
private String prefixName;
private Thread currentThread;
private Runnable runnable;
DelayTaskThread(String prefixName, long time,Runnable runnable) {
super(prefixName, time);
this.prefixName = prefixName;
this.runnable = runnable;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "has bean called");
this.currentThread = Thread.currentThread();
runnable.run();
}
Thread getCurrentThread() {
return currentThread;
}
}
重写ThreadPoolExecutor
/**
* TimeRecordThreadPoolExecutor
*
* @author zwd
* @date 2022/3/23
* @desc
*/
public class TimeOverInterceptThreadPoolExecutor extends ThreadPoolExecutor {
private final Logger log = Logger.getLogger("TimeOverInterceptThreadPoolExecutor");
private final ThreadLocal<Long> startTime = new ThreadLocal<>();
private final AtomicLong numTasks = new AtomicLong();
private final AtomicLong totalTime = new AtomicLong();
private DelayQueue<DelayTaskThread> delayQueue = new DelayQueue<>();
;
private final ReentrantLock r = new ReentrantLock();
public void init() {
new Thread(() -> {
for (; ; ) {
r.lock();
DelayTaskThread delayTaskThread = null;
try {
delayTaskThread = delayQueue.take();
}
catch (InterruptedException e) {
e.printStackTrace();
}
if (delayTaskThread != null && delayTaskThread.getCurrentThread() != null) {
log.info(String
.format("Thread %s has bean take form delayQueue, it will be interrupt", delayTaskThread
.getCurrentThread()));
delayTaskThread.getCurrentThread().interrupt();
}
r.unlock();
}
}).start();
}
public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
this.init();
}
public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
this.init();
}
public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
this.init();
}
public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
this.init();
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
log.info(String.format("Thread %s: start %s", t, r));
startTime.set(System.nanoTime());
}
@Override
public void execute(Runnable command) {
super.execute(command);
System.out.println("begin to execute");
DelayTaskThread delayTaskThread = (DelayTaskThread) command;
delayQueue.add(delayTaskThread);
log.info(String.format("Thread %s has been add tp delay queue", delayTaskThread.getCurrentThread()));
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
try {
long endTime = System.nanoTime();
long taskTime = endTime - startTime.get();
numTasks.incrementAndGet();
totalTime.addAndGet(taskTime);
log.info(String.format("Thread %s: end %s, time=%dns", t, r, taskTime));
}
finally {
DelayTaskThread delayTaskThread = (DelayTaskThread) r;
delayQueue.remove(delayTaskThread);
log.info(String.format("Thread %s has been remove from delay queue", delayTaskThread.getCurrentThread()));
super.afterExecute(r, t);
}
}
@Override
protected void terminated() {
try {
log.info(String.format("Terminated: avg time=%dns", totalTime.get() / numTasks.get()));
}
finally {
super.terminated();
}
}
}
测试
/**
* @date 2022/3/24
* @author zwd
* @email zwd@hhh.com
*/
public class TimeOverInterceptThreadPoolExecutorTest {
public static void main(String[] args) {
DelayTaskThread task1 = new DelayTaskThread("thread1", 10000,()-> {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
DelayTaskThread task2 = new DelayTaskThread("thread2", 100,()->{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
DelayTaskThread task3 = new DelayTaskThread("thread3", 100,()->{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
TimeOverInterceptThreadPoolExecutor poolExecutor = new TimeOverInterceptThreadPoolExecutor(10, 10, 0L, TimeUnit.MICROSECONDS,
new LinkedBlockingDeque<Runnable>(), new NameThreadFactory());
ArrayList<DelayTaskThread> delayTaskThreads = new ArrayList<>();
poolExecutor.execute(task1);
poolExecutor.execute(task2);
poolExecutor.execute(task3);
}
}
begin to execute
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor beforeExecute
信息: Thread Thread[pool-1-thread-1,5,main]: start DelayTask{name='thread1', time=10000}
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor execute
信息: Thread null has been add tp delay queue
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor execute
信息: Thread null has been add tp delay queue
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor beforeExecute
信息: Thread Thread[pool-1-thread-2,5,main]: start DelayTask{name='thread2', time=100}
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor beforeExecute
信息: Thread Thread[pool-1-thread-3,5,main]: start DelayTask{name='thread3', time=100}
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor execute
信息: Thread null has been add tp delay queue
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor afterExecute
信息: Thread null: end DelayTask{name='thread2', time=100}, time=2628800ns
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor afterExecute
信息: Thread null: end DelayTask{name='thread3', time=100}, time=2327300ns
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor afterExecute
信息: Thread Thread[pool-1-thread-2,5,main] has been remove from delay queue
三月 24, 2022 4:00:54 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor afterExecute
信息: Thread Thread[pool-1-thread-3,5,main] has been remove from delay queue
pool-1-thread-1has bean called
begin to execute
begin to execute
pool-1-thread-2has bean called
pool-1-thread-3has bean called
三月 24, 2022 4:01:04 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor lambda$init$0
信息: Thread Thread[pool-1-thread-1,5,main] has bean take form delayQueue, it will be interrupt
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutorTest.lambda$main$0(TimeOverInterceptThreadPoolExecutorTest.java:18)
at com.fire.threadpool.interceptLongTimeThread.DelayTaskThread.run(DelayTaskThread.java:22)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
三月 24, 2022 4:01:04 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor afterExecute
信息: Thread null: end DelayTask{name='thread1', time=10000}, time=9963947900ns
三月 24, 2022 4:01:04 下午 com.fire.threadpool.interceptLongTimeThread.TimeOverInterceptThreadPoolExecutor afterExecute
信息: Thread Thread[pool-1-thread-1,5,main] has been remove from delay queue
其中NameThreadFactory
/**
* NameThreadFactory
*
* @author zwd
* @date 2022/3/23
* @desc
*/
public class NameThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private final boolean daemon;
public NameThreadFactory(){
this("pool-" + poolNumber.getAndIncrement(),false);
}
public NameThreadFactory(String namePrefix) {
this(namePrefix, false);
}
public NameThreadFactory(String namePrefix, boolean daemon){
this.namePrefix = namePrefix + "-thread-";
this.daemon = daemon;
SecurityManager s = System.getSecurityManager();
group = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
}
@Override
public Thread newThread(Runnable r) {
String name = namePrefix + threadNumber.getAndIncrement();
Thread ret = new Thread(group, r, name, 0);
ret.setDaemon(daemon);
return ret;
}
public ThreadGroup getThreadGroup() {
return group;
}
}