java使用默认线程池踩过的坑(1)


场景

一个调度器,两个调度任务,分别处理两个目录下的txt文件,某个调度任务应对某些复杂问题的时候会持续特别长的时间,甚至有一直阻塞的可能。我们需要一个manager来管理这些task,当这个task的上一次执行时间距离现在超过5个调度周期的时候,就直接停掉这个线程,然后再重启它,保证两个目标目录下没有待处理的txt文件堆积。

问题

直接使用java默认的线程池调度task1和task2.由于外部txt的种种不可控原因,导致task2线程阻塞。现象就是task1和线程池调度器都正常运行着,但是task2迟迟没有动作。

当然,找到具体的阻塞原因并进行针对性解决是很重要的。但是,这种措施很可能并不能完全、彻底、全面的处理好所有未知情况。我们需要保证任务线程或者调度器的健壮性!

方案计划

线程池调度器并没有原生的针对被调度线程的业务运行状态进行监控处理的API。因为task2是阻塞在我们的业务逻辑里的,所以最好的方式是写一个TaskManager,所有的任务线程在执行任务前全部到这个TaskManager这里来注册自己。这个TaskManager就负责对于每个自己管辖范围内的task进行实时全程监控!

后面的重点就是如何处理超过5个执行周期的task了。

方案如下:

●一旦发现这个task线程,立即中止它,然后再次重启;

一旦发现这个task线程,直接将整个pool清空并停止,重新放入这两个task ——task明确的情况下】;

方案实施

中止后重启

Task实现类

  1. class FileTask extends Thread { 
  2. private long lastExecTime = 0
  3. protected long interval = 10000
  4. public long getLastExecTime() { 
  5.     return lastExecTime; 
  6. public void setLastExecTime(long lastExecTime) { 
  7.     this.lastExecTime = lastExecTime; 
  8. public long getInterval() { 
  9.     return interval; 
  10. public void setInterval(long interval) { 
  11.     this.interval = interval; 
  12. }  
  13. public File[] getFiles() { 
  14.     return null; 

Override

  1. public void run() { 
  2. while (!Thread.currentThread().isInterrupted()) { 
  3. lastExecTime = System.currentTimeMillis(); 
  4. System.out.println(Thread.currentThread().getName() + " is running -> " + new Date()); 
  5. try { 
  6. Thread.sleep(getInterval() * 6 * 1000); 
  7. } catch (InterruptedException e) { 
  8. Thread.currentThread().interrupt(); 
  9. e.printStackTrace();    // 当线程池shutdown之后,这里就会抛出exception了 
  10.             } 
  11.         } 
  12.     } 
  13.     

TaskManager

  1. public class TaskManager  implements Runnable { 
  2. private final static Log logger = LogFactory.getLog(TaskManager .class); 
  3. public Set<FileTask> runners = new CopyOnWriteArraySet<FileTask>(); 
  4. ExecutorService pool = Executors.newCachedThreadPool(); 
  5. public void registerCodeRunnable(FileTask process) { 
  6. runners.add(process); 
  7. public TaskManager (Set<FileTask> runners) { 
  8. this.runners = runners; 

@Override

  1. public void run() { 
  2.        while (!Thread.currentThread().isInterrupted()) { 
  3.            try { 
  4.                long current = System.currentTimeMillis(); 
  5.                for (FileTask wrapper : runners) { 
  6.                    if (current - wrapper.getLastExecTime() > wrapper.getInterval() * 5) { 
  7.                        wrapper.interrupt(); 
  8.                        for (File file : wrapper.getFiles()) { 
  9.                            file.delete(); 
  10.                        } 
  11.                     wrapper.start();   
  12.                    } 
  13.                } 
  14.            } catch (Exception e1) { 
  15.                logger.error("Error happens when we trying to interrupt and restart a task "); 
  16.                ExceptionCollector.registerException(e1); 
  17.            } 
  18.            try { 
  19.                Thread.sleep(500); 
  20.            } catch (InterruptedException e) { 
  21.            } 
  22.        } 
  23.    } 
  24.     

这段代码会报错 java.lang.Thread IllegalThreadStateException。为什么呢?其实这是一个很基础的问题,您应该不会像我一样马虎。查看Thread.start()的注释, 有这样一段:

It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

是的,一个线程不能够启动两次。那么它是怎么判断的呢?

  1. public synchronized void start() { 
  2.         /** 
  3.          * A zero status value corresponds to state "NEW".    0对应的是state NEW 
  4.          */ 

if (threadStatus != 0) //如果不是NEW state,就直接抛出异常!




相关内容