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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangjikuan/article/details/78670288

前言

在写多线程程序时,大多数情况下会先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 1
thread 1, refreshTimes is 3
thread 2, refreshTimes is 3
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944046190, 1=1511944045188, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 1002
thread 2, refreshTimes is 1003
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944047189, 1=1511944045188, 2=1511944045188}
thread 0, refreshTimes is 1
thread 1, refreshTimes is 2002
thread 2, refreshTimes is 2002
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944048191, 1=1511944045188, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 3004
thread 2, refreshTimes is 3004
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944049193, 1=1511944045188, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 4005
thread 2, refreshTimes is 4005
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944050193, 1=1511944045188, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 5005
thread 2, refreshTimes is 5006
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944051193, 1=1511944051193, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 0
thread 2, refreshTimes is 6006
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944052190, 1=1511944051193, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 999
thread 2, refreshTimes is 7004
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944053192, 1=1511944051193, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 1999
thread 2, refreshTimes is 8005
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944054193, 1=1511944051193, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 3000
thread 2, refreshTimes is 9005
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944055192, 1=1511944051193, 2=1511944045188}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 3999
thread 2, refreshTimes is 10004
alive thread 2: is 10004ms to refresh, will restart
thread 2 into InterruptedException, over
worker thread#4 start...
restart success, thread id is:4
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944056193, 1=1511944051193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 5000
thread 4, refreshTimes is 1001
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944057191, 1=1511944051193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 5998
thread 4, refreshTimes is 1998
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944058193, 1=1511944057193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 1000
thread 4, refreshTimes is 3000
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944059193, 1=1511944057193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 2000
thread 4, refreshTimes is 4000
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944060192, 1=1511944057193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 2999
thread 4, refreshTimes is 4999
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944061194, 1=1511944057193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 4002
thread 4, refreshTimes is 6002
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944062191, 1=1511944057193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 4998
thread 4, refreshTimes is 6998
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944063191, 1=1511944057193, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 5999
thread 4, refreshTimes is 7999
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944064192, 1=1511944063196, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 996
thread 4, refreshTimes is 8999
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944065193, 1=1511944063196, 4=1511944055193}
thread 0, refreshTimes is 0
thread 1, refreshTimes is 1998
thread 4, refreshTimes is 10001
alive thread 4: is 10001ms to refresh, will restart
restart success, thread id is:5
worker thread#5 start...
thread 4 into InterruptedException, over
monitor thread start..., aliveThreadRefreshTimeMap:{0=1511944066193, 1=1511944063196, 5=1511944065194}
thread 0, refreshTimes is 1
thread 1, refreshTimes is 2998
thread 5, refreshTimes is 1000

猜你喜欢

转载自blog.csdn.net/zhangjikuan/article/details/78670288