线程池中多线程设置超时退出监控
来源:互联网 发布:淘宝网狗狗 编辑:程序博客网 时间:2024/06/05 23:55
前言
在写多线程程序时,大多数情况下会先excutor创建线程池,然后再创建线程,但是对一些读数据库或者其他IO操作,容易堵住线程,此时就需要给线程设置超时时间,干掉超时的线程再重新拉起一个线程来,但是java线程创建并没有预留超时参数,研究了一下网上也没找到好的解决方案,干脆自己想办法搞了一个。
方案
监控线程往往有这么几种方案
- 首先想到的应该就是future的get方法,有超时时间设置参数,但是这个get方法是阻塞的,对于那种等待线程运行结果的需求使用起来还是比较方便的,但是对线程监控使用起来就各种不爽了,尤其是要监控多个线程的时候,例如下面的文章,创建几个线程然后再创建相同数量的监控线程 http://blog.csdn.net/dj2442945707/article/details/56292413
- 再一个就是比较普通的,任务中if标记位,在其他任务中set标记位使任务退出,此方法是线程退出的一种方法,但是无法做到超时自动退出
- 另一种是利用中断,也就是让线程抛出InterruptException来终止线程,比如future中的cancel方法就是利用中断方式向线程发送信号的,但是此种方法可以另耗io的线程退出,无法让一直耗费CPU的线程退出
需求
项目需要,需要完成如下的需求
- 1.线程池创建多线程
- 2.对创建的线程有超时检测
- 3.另超时的线程退出执行,释放线程池中的线程
4.一个线程超时退出了,需要对应的再拉起一个线程来确保任务能继续执行
完成上面的需求实际上就是线程超时、线程退出、线程创建的结合使用
实现
代码如下:
package com.alibaba.dbtech.paas.app.adha.test;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;import org.junit.Test;public class ThreadMonitorTimeoutTest { private ConcurrentMap<Integer, Long> aliveThreadRefreshTimeMap = new ConcurrentHashMap<>(); final Map<Integer, Future<?>> aliveThreadFutureMap = new HashMap<>(); private int aliveThreadNum = 0; private ExecutorService cachedThreadPool; private void doOtherThing(int mseconds) throws InterruptedException { for (int i = 0; i < 1000; i++) { int j = i; } Thread.sleep(mseconds); } private Runnable workerThread(int i, int sleepTime) { return new Runnable() { @Override public void run() { try { long currentTime = System.currentTimeMillis(); aliveThreadRefreshTimeMap.put(i, currentTime); System.out.printf("worker thread#%s start...\n", i); while (true) { doOtherThing(sleepTime); currentTime = System.currentTimeMillis(); aliveThreadRefreshTimeMap.replace(i, currentTime); } } catch (InterruptedException e) { System.out.printf("thread %d into InterruptedException, over\n", i); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }; } Runnable monitorWorker = new Runnable() { @Override public void run() { try { System.out.printf("monitor thread start..., aliveThreadRefreshTimeMap:%s\n", aliveThreadRefreshTimeMap); List<Integer> removeIdList = new ArrayList<>(); for (int threadId : aliveThreadRefreshTimeMap.keySet()) { long currentTime = System.currentTimeMillis(); long refreshTimes = currentTime - aliveThreadRefreshTimeMap.get(threadId); System.out.printf("thread %d, refreshTimes is %d\n", threadId, refreshTimes); if (refreshTimes > 10000) { System.out.printf("alive thread %d: is %dms to refresh, will restart\n", threadId, currentTime - aliveThreadRefreshTimeMap.get(threadId)); aliveThreadFutureMap.get(threadId).cancel(true); aliveThreadNum ++; Future<?> future = cachedThreadPool.submit(workerThread(aliveThreadNum, aliveThreadNum*4000)); aliveThreadFutureMap.put(aliveThreadNum,future); removeIdList.add(threadId); System.out.printf("restart success, thread id is:%d\n", aliveThreadNum); } } for (int id : removeIdList) { aliveThreadFutureMap.remove(id); aliveThreadRefreshTimeMap.remove(id); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }; @Test public void createTask() { cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 3; i++) { // aliveCachedThreadPool.execute(adhaAliveDetectTask); Future<?> future = cachedThreadPool.submit(workerThread(i, i*6000)); aliveThreadFutureMap.put(i, future); aliveThreadNum++; } // detect monitor task ScheduledExecutorService monitorExecutor = Executors.newScheduledThreadPool(1); monitorExecutor.scheduleAtFixedRate(monitorWorker, 0, 1, TimeUnit.SECONDS); while(true); }}
解释:
一、createTask
createTask(): 创建线程池,并启动3个线程来执行workerThread
Future<?> future = cachedThreadPool.submit(workerThread(i, i*6000));
使用future的目的就是后期监控中可以直接调用cancel方法终止线程,之所以是i*6000是验证不超时的线程和超时的线程的不同运行状态
monitorExecutor 创建一个定时调度线程,来执行监控,注意这里使用单个线程来监控Excutor线程池中创建的所有线程
二、workerThread线程
一直循环的方式不停的执行某些耗时操作,没什么可解释的
三、monitorWorker监控线程
监控线程是本程序的重点
整体逻辑就是:
C程序中看门狗喂狗思想,给每一个线程一个唯一标识,也就是i,每个线程中维护了一个全局变量,就是当前时间戳,每个循环更新一次最新的时间戳,监控线程中通过读这个时间戳(非阻塞),来判断线程是否超时,如果超时调用future.cancel来取消线程运行,但是线程池中的多个线程future又该如何跟超时线程对应起来呢,仍然是自己定义的唯一的线程id(i),如何退出超时线程呢?这里使用的中断异常,收到中断信号抛出异常从而跳出循环
四、全局变量
aliveThreadRefreshTimeMap: 维护每个线程id和时间戳的映射,用来监控哪个线程超时用的
aliveThreadFutureMap:维护每个线程id和线程返回值future的映射,用来取消超时线程运行使用
aliveThreadNum:记录着最后创建的线程的id位置,用于重新拉起新的线程时仍然使用不重复的id号用的,可以区分当前线程是由于超时重新拉起的还是启动时创建的,不要纠结那个没有3号线程的问题,只要不重复就好
cachedThreadPool:任然在cachePool线程池中拉起新线程,之所以用newCachedThreadPool就是看好他的重复使用线程,退出的线程空闲时可以让新的线程重复使用
打印结果
worker thread#0 start...worker thread#2 start...worker thread#1 start...monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944045190, 1=1511944045188, 2=1511944045188}thread 0, refreshTimes is 1thread 1, refreshTimes is 3thread 2, refreshTimes is 3monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944046190, 1=1511944045188, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 1002thread 2, refreshTimes is 1003monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944047189, 1=1511944045188, 2=1511944045188}thread 0, refreshTimes is 1thread 1, refreshTimes is 2002thread 2, refreshTimes is 2002monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944048191, 1=1511944045188, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 3004thread 2, refreshTimes is 3004monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944049193, 1=1511944045188, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 4005thread 2, refreshTimes is 4005monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944050193, 1=1511944045188, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 5005thread 2, refreshTimes is 5006monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944051193, 1=1511944051193, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 0thread 2, refreshTimes is 6006monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944052190, 1=1511944051193, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 999thread 2, refreshTimes is 7004monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944053192, 1=1511944051193, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 1999thread 2, refreshTimes is 8005monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944054193, 1=1511944051193, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 3000thread 2, refreshTimes is 9005monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944055192, 1=1511944051193, 2=1511944045188}thread 0, refreshTimes is 0thread 1, refreshTimes is 3999thread 2, refreshTimes is 10004alive thread 2: is 10004ms to refresh, will restartthread 2 into InterruptedException, overworker thread#4 start...restart success, thread id is:4monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944056193, 1=1511944051193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 5000thread 4, refreshTimes is 1001monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944057191, 1=1511944051193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 5998thread 4, refreshTimes is 1998monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944058193, 1=1511944057193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 1000thread 4, refreshTimes is 3000monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944059193, 1=1511944057193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 2000thread 4, refreshTimes is 4000monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944060192, 1=1511944057193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 2999thread 4, refreshTimes is 4999monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944061194, 1=1511944057193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 4002thread 4, refreshTimes is 6002monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944062191, 1=1511944057193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 4998thread 4, refreshTimes is 6998monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944063191, 1=1511944057193, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 5999thread 4, refreshTimes is 7999monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944064192, 1=1511944063196, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 996thread 4, refreshTimes is 8999monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944065193, 1=1511944063196, 4=1511944055193}thread 0, refreshTimes is 0thread 1, refreshTimes is 1998thread 4, refreshTimes is 10001alive thread 4: is 10001ms to refresh, will restartrestart success, thread id is:5worker thread#5 start...thread 4 into InterruptedException, overmonitor thread start..., aliveThreadRefreshTimeMap:{0=1511944066193, 1=1511944063196, 5=1511944065194}thread 0, refreshTimes is 1thread 1, refreshTimes is 2998thread 5, refreshTimes is 1000
- 线程池中多线程设置超时退出监控
- java 多线程 线程返回值 子线程超时时间设置
- Java 多线程设置线程超时时间之 Callable接口和Future接口 线程超时控制
- Java 多线程设置线程超时时间之 Callable接口和Future接口 超时控制
- ROS 监控制线程通过setDaemon 设置为随启动线程退出
- 在MFC 子线程中使用UI(控件)退出时死锁或者超时处理参考
- 设置Linux shell超时自动退出
- libcurl多线程超时设置不安全
- libcurl多线程超时设置不安全
- 设置超时,处理cookie,多线程
- libcurl多线程超时设置不安全
- java多线程设置超时时间
- Java 多线程设置线程超时时间之 Callable接口和Future接口
- Java 多线程设置线程超时时间之 Callable接口和Future接口
- Java 多线程之线程监控
- 多线程开发之线程的超时
- 强行关掉超时的线程还是让它自己退出
- 多线程设置超时 测试端口是否打开
- PrimeNG ——Let Filtering, Sorting and Lazy loading work together!
- 字节全讲解
- Java web 入门
- No qualifying bean of type(xxxxxx)
- HDOJ 2521 反素数
- 线程池中多线程设置超时退出监控
- 用Python从新浪下载A股复权因子信息
- 剑指offer——斐波那契数列
- 今天第一次写
- maven的常用命令
- XMind 8 Update 6 Pro官方版下载附序列号
- 【git】pull、fetch 区别
- android lowmemorykiller笔记
- Linux--Xshell脚本