记学习线程池过程中遇到的一个坑

这篇实际上是跟上一篇想实现的功能一样,想完成线程状态的跟踪。但是为什么想采用这种方式来进行跟踪呢?主要是前面都是在try catch中捕获异常,然后因为异常导致的线程终止。那么其实实际的开发过程中,遇到的问题都不是前面遇到的问题,所以只能通过心跳的方式进行监控。

通过学习线程池,了解到了:

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

这个线程池可以完成实现心跳检测的功能。

所以就想想用它来实现一个监听器的功能,定时的监听我开启的线程状态,如果某个线程出现异常或者死掉,就将该死掉的线程给重新启动。

于是就有了下面的代码:

监听器类:

import java.util.Map;
import java.util.concurrent.*;

/**
 * 心跳检测,对线程进行监控。
 */
public class ThreadListener{

    private ThreadPoolExecutor executor;
    //线程容器key:线程名称,value:线程
    private Map<String,Thread> threadMap;

    public ThreadListener(){

    }
    public ThreadListener(ThreadPoolExecutor poolExecutor,Map threadMap){
        this.executor = poolExecutor;
        this.threadMap = threadMap;
    }

    //启动监听器方法,每2s执行一次监听
    public void startListening(){
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            public void run() {
                for (String s : threadMap.keySet()) {
                    System.out.println("线程:"+s+",状态:"+threadMap.get(s).isAlive());
                }

            }
        },2,2,TimeUnit.SECONDS);
    }
}

线程类:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 通过心跳检测的方式时刻抓取线程池的线程状态
 */
public class ThreadTasks {


    public static void main(String[] args) {
        ThreadTasks t = new ThreadTasks();
        t.ttt();
    }

    public void ttt(){
        Map<String,Thread> threadMap = new HashMap<String,Thread>();
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 2,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
        for (int i = 1; i <= 2; i++) {
            TempTask tempTask = new TempTask("md"+i);
            Thread task = new Thread(tempTask,tempTask.taskName);
            poolExecutor.execute(task);
            threadMap.put(tempTask.taskName,task);
        }
        //将线程纳入到监听中
        ThreadListener threadListener = new ThreadListener(poolExecutor,threadMap);
        threadListener.startListening();

    }
     class TempTask implements Runnable{

        private String taskName;
        public TempTask(){

        }
        public TempTask(String taskName){
            this.taskName = taskName;
        }

        int count = 0;
        public void run() {
            while (true){
                try {
                    Thread.sleep(1000);
                    count++;
                    if("md1".equals(taskName) && count == 5){
                        System.out.println("抛出异常");
                        throw new RuntimeException("我是异常..");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(taskName+"正在运行...");
            }
        }
    }

}

好了,就是这么个简单的东西。本想着可以监听到线程的死亡状态。没曾想运行结果是这样的:

md1正在运行...
md2正在运行...
md1正在运行...
md2正在运行...
md1正在运行...
md2正在运行...
md1正在运行...
md2正在运行...
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 我是异常..
    at com.listener.ThreadTasks$TempTask.run(ThreadTasks.java:50)
    at java.lang.Thread.run(Thread.java:748)
    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)
抛出异常
md2正在运行...
线程:md2,状态:false
线程:md1,状态:false
md2正在运行...
md2正在运行...
md2正在运行...
md2正在运行...
md2正在运行...
线程:md2,状态:false
线程:md1,状态:false
md2正在运行...
md2正在运行...

呃,为啥都是false。

细心的大家会发现,这里出现异常的线程是咱们定义的线程么,咱们定义的线程名称不是md1和md2么。通过源码,才发现原来线程池执行的线程并不是咱们传递进去的线程,而是重新从线程工厂取的。

//firstTask为传递的线程
w = new Worker(firstTask);
//t为开启的线程
final Thread t = w.thread;
Worker(Runnable firstTask) {
      setState(-1); // inhibit interrupts until runWorker
      this.firstTask = firstTask;
      //实际上启动的是这个
      this.thread = getThreadFactory().newThread(this);
}

到此,美好的想法彻底没戏了。那么此时我该如何监听呢?

这里想到的办法就是舍弃线程池,对上面的代码进行改造:
直接通过new Thread()的形式进行创建,同时将线程和任务分别存放在不同的map中。然后再监听器中判断状态,如果发现线程死掉,就重新启动一个线程,然后执行任务。

public class ThreadTasks {

    public static void main(String[] args) {
        ThreadTasks t = new ThreadTasks();
        t.tttt();
    }

    public void tttt(){
        Map<String,Thread> threadMap = new HashMap<String,Thread>();
        Map<String,TempTask> taskMap = new HashMap<String,TempTask>();

        for (int i = 1; i <= 2; i++) {
            TempTask tempTask = new TempTask("md"+i);
            Thread t = new Thread(tempTask,tempTask.taskName);
            t.start();
            threadMap.put(tempTask.taskName,t);
            taskMap.put(tempTask.taskName,tempTask);
        }
        //将线程纳入到监听中
        ThreadListener threadListener = new ThreadListener(null,threadMap,taskMap);
        threadListener.startListening();

    }


     class TempTask implements Runnable{
        private String taskName;
        public TempTask(){}
        public TempTask(String taskName){
            this.taskName = taskName;
        }

        int count = 0;
        public void run() {
            while (true){
                try {
                    Thread.sleep(1000);
                    count++;
                    if("md1".equals(taskName) && count == 5){
                        count++;
                        //throw new RuntimeException("我是异常..");
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(taskName+"正在运行...");
            }
            System.out.println(taskName+"停止运行...");
        }
    }

}
public class ThreadListener{

    private ThreadPoolExecutor executor;
    private Map<String,Thread> threadMap;
    private Map<String,ThreadTasks.TempTask> taskMap;

    public ThreadListener(){

    }
    public ThreadListener(ThreadPoolExecutor poolExecutor,Map threadMap,Map taskMap){
        this.executor = poolExecutor;
        this.threadMap = threadMap;
        this.taskMap = taskMap;
    }

    public void startListening(){
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            public void run() {
                for (String s : threadMap.keySet()) {
                    boolean isAlive = threadMap.get(s).isAlive();
                    System.out.println("线程:"+s+",状态:"+isAlive);
                    if(!isAlive){
                        threadMap.remove(s);//将死掉的线程清理
                        System.out.println("重启线程"+s);
                        Thread t = new Thread(taskMap.get(s),s);
                        threadMap.put(s,t);

                        executorService.execute(t);
                    }
                }

            }
        },5,5,TimeUnit.SECONDS);
    }
}

这种方式感觉有点儿low,有更好的方法可留言哈。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值