创建一个不退出

时间:2016-06-16 05:18:58

标签: java multithreading executorservice threadpoolexecutor

我想知道创建一个不终止的Java Thread的最佳方法是什么。 目前,我基本上有一个" Runner"基本上看起来像:

ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < numThreads; ++i) {
    pool.submit(new Task());
}
pool.shutdown();

和Task看起来像这样

public class Task {
    ...
    public void run() {
        while(true) { }
    }
}

我的方法有两个问题:

  1. 我是否应该创建一个在工作后才返回的任务并继续生成执行最少量工作的线程?我担心开销,但不知道如何衡量。

  2. 如果我有一个无限循环的线程,当我强制退出可执行文件时,这些线程会被关闭并清理吗?经过一些测试后,当强制关闭包含ExecutorService的代码时,不会出现InterruptException。

  3. 编辑: 详细说明,该任务看起来像

    public void run() {
        while(true) {
            // Let queue be a synchronized, global queue
            if (queue has an element) {
                // Pop from queue and do a very minimal amount of work on it
                // Involves a small amount of network IO (maybe 10-100 ms)
            } else {
                sleep(2000);
            }
        }
    }
    

1 个答案:

答案 0 :(得分:0)

我同意@D Levant,阻止队列是这里使用的关键。使用阻塞队列,您无需处理队列空或队列已满的情况。

在Task类中,

while(true) {
        // Let queue be a synchronized, global queue
        if (queue has an element) {
            // Pop from queue and do a very minimal amount of work on it
            // Involves a small amount of network IO (maybe 10-100 ms)
        } else {
            sleep(2000);
        }
    }

它真的不是一个好方法,它效率低,因为你的while循环是连续轮询,即使你已经把线程设置为sleep(),但它仍然是每次线程唤醒和睡眠时不必要的上下文切换的开销

在我看来,你使用Executors的方法看起来很适合你的情况。线程创建显然是一个代价高昂的过程,Executors为我们提供了为不同任务重用相同线程的灵活性。 您可以通过execute(Runnable)submit(Runnable/Callable)完成任务,然后由执行人员在内部处理休息。 Executors仅在内部使用阻塞队列概念。

您甚至可以使用ThreadPoolExecutor类创建自己的线程池,并在其构造函数中传递所需的参数,在这里您可以传递自己的阻塞队列来保存任务。其余的线程管理将根据构造函数中的配置传递来处理,因此,如果您对配置参数非常有信心,那么您可以继续使用它。

现在最后一点,如果您不想使用Java内置的Executors框架,那么您可以使用BlockingQueue来设计解决方案来保存任务并启动一个线程,该线程将从此阻塞队列中获取任务执行,下面是高级实现:

class TaskRunner {
   private int noOfThreads;  //The no of threads which you want to run always
   private boolean started;
   private int taskQueueSize; //No. of tasks that can be in queue at a time, when try to add more tasks, then you have to wait.
   private BlockingQueue<Runnable> taskQueue;
   private List<Worker> workerThreads;

   public TaskRunner(int noOfThreads, int taskQueueSize) {
      this.noOfThreads = noOfThreads;
      this.taskQueueSize = taskQueueSize;
   }

  //You can pass any type of task(provided they are implementing Runnable)
   public void submitTask(Runnable task) {
      if(!started) {
         init();
      }
      try {
         taskQueue.put(task);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }

   public void shutdown() {
      for(Worker worker : workerThreads){
         worker.stopped = true;
      }
   }

   private void init() {
      this.taskQueue = new LinkedBlockingDeque<>(taskQueueSize);
      this.workerThreads = new ArrayList<>(noOfThreads);
      for(int i=0; i< noOfThreads; i++) {
         Worker worker = new Worker();
         workerThreads.add(worker);
         worker.start();
      }
   }

   private class Worker extends Thread {
      private volatile boolean stopped;
      public void run() {
         if(!stopped) {
            try {
               taskQueue.take().run();
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
}

class Task1 implements Runnable {
   @Override
   public void run() {
      //Your implementation for the task of type 1
   }
}

class Task2 implements Runnable {
   @Override
   public void run() {
      //Your implementation for the task of type 2
   }
}

class Main {

   public static void main(String[] args) {
      TaskRunner runner = new TaskRunner(3,5);
      runner.submitTask(new Task1());
      runner.submitTask(new Task2());
      runner.shutdown();
   }
}