2016-06-16 2 views
0

Мне было интересно, какой лучший способ создать поток Java, который не завершается. В настоящее время я в основном имеют «Runner», который в основном выглядит как:Создание потока, который не выходит

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

и задачи выглядит примерно так

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

Есть две проблемы, я с моим подходом:

  1. Должен ли я создавать задачу, которая просто возвращается после выполнения работы, и продолжать нерестовые потоки, которые выполняют минимальный объем работы? Меня беспокоит накладные расходы, но я не уверен, как его измерить.

  2. Если у меня есть Thread, который просто бесконечно петляет, когда я принудительно завершаю исполняемый файл, будут ли эти потоки отключены и очищены? После некоторого тестирования не возникает прерывание прерывания, когда код, содержащий ExecutorService, принудительно завершается.

EDIT: Выработать Целевая выглядит

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); 
     } 
    } 
} 
+0

Оба вопроса действительно зависят от того, что находится внутри блока 'while (true) {}'. Это вращение, сон, блокировка ввода-вывода или что? – erickson

+1

бессмысленно создавать пул потоков, но вы просто запускаете forever-loop для каждого потока. Для того, что вы собираетесь делать, вероятно, лучше подумать: иметь один выделенный поток для чтения из очереди.Всякий раз, когда он получает элемент, обработайте этот элемент, создав короткую задачу «Задача» и выполнив ее с пулом потоков. –

+0

. Эй, @erickson: просто добавлено редактирование. В принципе, он будет спать или блокироваться в сети IO (я ожидаю, что вычисление будет минимальным) – Lycus

ответ

0

Я согласен с @d Леванта, Блокировка очереди является ключом для использования здесь. С блокировкой очереди вам не нужно обрабатывать сценарий с пустой или пустой очередью.

В классе задач,

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); 
     } 
    } 

Его действительно не хороший подход, его неэффективным, потому что ваш цикл в то время непрерывно опроса, даже вы поставили нить сна(), но до сих пор его также накладные расходы ненужные контекстные переключатели каждый раз, когда поток пробуждается и спит.

На мой взгляд, ваш подход к использованию Исполнителей выглядит хорошо для вашего дела. Создание потоков, очевидно, является дорогостоящим процессом, а исполнители предоставляют нам гибкость повторного использования одного и того же потока для разных задач. Вы можете просто передать свое задание через execute(Runnable) или submit(Runnable/Callable), а затем остальное позаботится об исполнителях внутри страны. Исполнители внутренне используют только концепцию блокировки очереди.

Вы даже можете создать свой собственный пул потоков, используя класс ThreadPoolExecutor и передав требуемый параметр в его конструкторе, здесь вы можете передать свою собственную блокирующую очередь для выполнения задач. Управление потоком остатка будет позаботиться об этом на основе конфигурационных проходов в конструкторе, поэтому, если вы действительно уверены в параметрах конфигурации, вы можете пойти на это.

Теперь последнее, если вы не хотите использовать встроенную среду исполнителей Java, тогда вы можете спроектировать свое решение, используя 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(); 
    } 
} 
Смежные вопросы