java线程池中的线程超时控制

探讨使用JDK线程池实现任务超时管理的方法,包括如何避免启动额外线程进行监控,以及如何优雅地中断长时间运行的任务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   先MARK下一篇将JDK自带线程池的工具文,讲得挺清楚。

  http://www.oschina.net/question/565065_86540

   一些系统中的外部IO调用,比如调用第三方系统的WEBSERVICE等,尽管可以设定超时时间,但若每次调用都接近超时的上限的话,在并发较大的情况下很容易会造成系统不堪重负。此时若此调用无需实时获得调用结果,则可采用newFixedThreadPool将并发调用数限制在一定数量下,其他任务自动进入内存队列。

    而开放给第三方系统的接口,在被调用时若处理较耗时,则可采用先放入线程池并立即返回的异步处理方式提高系统吞吐量。

    同时,JAVA线程池也可以同时配合可持久化的任务队列来来防止任务的丢失。

 

     最近,项目又提出,在某些场景下,还要加入线程超时机制。

     于是百度之,发现被转载最多的整体思路就是: 每启动一个线程的时候,通过join(long timeout)方法或者其他类似方法(比如java.util.concurrent.Future.get(long timeout, TimeUnit unit) )启动一个监听线程,当监听线程通过上述方法判断任务线程超时,再调用interrupt或者cancel方法退出线程。

 

      上述解决方案仔细看了下,有如下2个问题:

       1.这种方式,每执行一个任务要启动2个线程,代价未免也太大了

     个人解决方式:

          对于每种类型的线程池,完全可以采用上文中的JDK自带线程池的实现,在提交任务的时候通过如下方法获得一个Future参数。

         <T> Future<T> java.util.concurrent.ExecutorService.submit(Callable<T> task)

           而对于每一个task,实现一个可获取任务启动时间的接口。       

           将该Future对象和提交的task对象放置于一个集合对象中,并启动一个独立线程,每隔一段时间(比如1秒)遍历该集合读写,并调用 boolean java.util.concurrent.Future.isDone()方法判断是否执行完成,若未完成,则比较task的启动时间和当前时间,若超时了,则启动关闭线程的操作。

        2.如何关闭一个线程

            这是前文百度到的解决方案中最误导人的地方。

           首先JAVA目前的中断模型是协作式的,调用interrupt()或者cancel()方法,只会在执行线程中设置一个中断标志位。是否真正要中断,则要看具体程序的逻辑。

           默认可中断的JAVA操作,主要集中于那些会让线程阻塞的操作。比如sleep(),wait()等。有个简单的判别办法,就是方法声明中是否有抛出中断异常,比如Thread.sleep(long millis) throws InterruptedException

而诸如IO操作或者同步时锁获取等阻塞,则不可被中断(当然,java nio或者JDK1.5的显式的Lock方式有提供了支持中断的方法)。

           前文百度到的范例代码,都是通过sleep()作为DEMO,这就是为什么很多人抱怨实际不起作用的原   因。

           那解决方式呢? 几乎没有。强制不由分说的关闭一个线程可能会导致各种问题,比如锁不释放等。一个比较偏门的方式就是让任务代码自己实现一个关闭资源的方法,超时时由监听线程调用。比如强制close掉socket,则正在调用中的IO操作则会立刻抛出异常。这种解决方式依赖于任务线程本身的实现,的确也不是什么高明的方式。

            在此求助下,如果各位有更好的关闭线程的方式,请不吝赐教!

 

  

   

### Java 中线中的线程超时机制及其使用场景 #### 线程超时的作用 在Java中,线程池通过设置线程的最大空闲时间来控制资源的有效利用。如果某个线程在指定的时间内未被使用,则该线程会被终止以释放资源。这种机制有助于防止过多的线程占用内存和其他系统资源。 #### 使用场景分析 1. **高并发请求处理** 当应用序接收到大量瞬时请求时,可以配置较大的线程池大小以便快速响应这些请求。然而,在高峰期过后,保持这么多活跃线程是没有必要的。此时可以通过设定合理的`keepAliveTime`参数让多余的线程自动销毁[^2]。 2. **定时任务执行** 对于一些周期性的后台作业,如日志清理、缓存刷新等操作,通常不需要一直维持固定数量的工作线程。相反,可以在每次任务完成后允许工作线程等待一段时间再关闭它们,从而节省不必要的开销[^3]. 3. **长时间运行的服务端应用** 长期在线的应用可能会经历流量波动的情况。为了适应不同的负载情况,合理调整线程池内的活动成员数目就显得尤为重要。适当缩短闲置线程存活期限可以帮助更好地应对突发状况下的性能需求变化[^4]. #### 实现方式举例 下面是一个简单的例子展示了如何定义带有超时特性的自定义线程工厂: ```java import java.util.concurrent.*; public class CustomThreadPoolExecutor { private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 10; private static final long KEEP_ALIVE_TIME = 60L; // 设置为60秒 public static void main(String[] args) throws InterruptedException { ThreadPoolExecutor executorService = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), Executors.defaultThreadFactory()); Runnable task = () -> System.out.println(Thread.currentThread().getName() + " is executing"); for (int i = 0; i < 15; ++i){ executorService.execute(task); } Thread.sleep(70_000); // 让主线程休眠足够长的时间观察效果 executorService.shutdown(); while (!executorService.awaitTermination(5, TimeUnit.SECONDS)){ System.err.println("Waiting for tasks to finish..."); } } } ``` 在这个示例序里,设置了最大空闲时间为60秒(`KEEP_ALIVE_TIME`),这意味着任何超过这个时间段没有工作的额外线程将会被回收掉。这不仅提高了系统的灵活性也减少了不必要的资源浪费。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值