2012-04-26 6 views
3

Я хотел бы выполнить некоторые работы в фоновом режиме с ограничением по времени. Дело в том, что я не хочу блокировать основной поток.Тайм-аут для ExecutorService без блокировки основного потока

Наивная реализация должна иметь два исполнителя. Один для планирования/таймаута, а второй - для выполнения работы.

final ExecutorService backgroundExecutor = Executors.newSingleThreadExecutor(); 
final ExecutorService workerExecutor = Executors.newCachedThreadExecutor(); 


backgroundExecutor.execute(new Runnable() { 
    public void run() { 
     Future future = workerExecutor.submit(new Runnable() { 
      public void run() { 
       // do work 
      } 
     }); 
     try { 
      future.get(120 * 1000, TimeUnit.MILLISECONDS); 
     } catch (InterruptedException e) { 
      logger.error("InterruptedException while notifyTransactionStateChangeListeners()", e); 
      future.cancel(true); 
     } catch (ExecutionException e) { 
      logger.error("ExecutionException", e); 
     } catch (TimeoutException e) { 
      logger.error("TimeoutException", e); 
      future.cancel(true); 
     } 
    } 
}); 

Есть ли другие решения?

+0

Одним из упрощений будет один пул из двух потоков, один из которых выполнит задание, а другой ждет его. Это, по крайней мере, экономит внутренний пул, но в остальном это не очень помогает. – Gray

+0

Можно ли одновременно выполнять задачи как для Таймера, так и для Исполнителя? Сначала вы получите в run(), и поэтому где-то понадобится блокировка или синхронизация для арбитража между тайм-аутом и завершенной задачей, чтобы в каждом случае можно было предпринять правильные действия. Хм .. не уверен. –

+0

Поскольку вы игнорируете результат Future.get() в своем коде, я задаюсь вопросом, действительно ли вам нужно ждать результата в каком-то потоке? –

ответ

2

Вам не нужен ExecutorService, чтобы запустить один поток за один раз. Вместо этого вы можете создать FutureTask, который дает вам те же преимущества без накладных расходов.

FutureTask<T> future = new FutureTask<T>(callable); 
Thread thread = new Thread(future); 
thread.start(); 
try { 
    future.get(120 * 1000, TimeUnit.MILLISECONDS); 
} ... 

Вызов в приведенном выше фрагменте будет вашей задачей. Если у вас есть Runnable (как вы делаете в вашем выше блоке кода), вы можете превратить его в Callable через:

Callable callable = Executors.callable(runnable, null); 

Таким образом, чтобы подвести итог, что ваш код может измениться на:

backgroundExecutor.execute(new Runnable() { 
    public void run() { 

     Runnable myRunnable = new Runnable() { 
      public void run() { 
       // do work 
      } 
     } 

     Callable callable = Executors.callable(myRunnable, null); 

     FutureTask<T> future = new FutureTask<T>(callable); 
     Thread thread = new Thread(future); 
     thread.start(); 

     try { 
      future.get(120 * 1000, TimeUnit.MILLISECONDS); 
     } catch (InterruptedException e) { 
      logger.error("InterruptedException while notifyTransactionStateChangeListeners()", e); 
      future.cancel(true); 
     } catch (ExecutionException e) { 
      logger.error("ExecutionException", e); 
     } catch (TimeoutException e) { 
      logger.error("TimeoutException", e); 
      future.cancel(true); 
     } 
    } 
}); 

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

Смежные вопросы