线程池中多线程设置超时退出监控

来源:互联网 发布:淘宝网狗狗 编辑:程序博客网 时间: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
原创粉丝点击