Является ли Future.get () заменой для Thread.join ()? - PullRequest
8 голосов
/ 06 марта 2009

Я хочу написать демон командной строки, который будет работать вечно. Я понимаю, что если я хочу, чтобы JVM могла корректно завершать работу в Linux, нужно обернуть загрузчик через некоторый C-код. Я думаю, что я буду в порядке с отключением крюка на данный момент.

На мои вопросы:

  1. Мой основной блок (String []) запустит отдельного Супердемона.
  2. Супердемон будет опрашивать и зацикливаться вечно.

Так что обычно я делал бы:

class Superdaemon extends Thread { ... }

class Bootstrap
{
    public static void main( String[] args )
    {
        Thread t = new Superdaemon();
        t.start();

        t.join();
    }
}

Теперь я подумал, что если я запустил Superdaemon через Executor, я могу сделать

Future<?> f = exec.submit( new Superdaemon() );

f.get();

Реализован ли Future.get() с помощью Thread.join ()? Если нет, то ведет ли он себя эквивалентно?

С уважением,

Ашитак

Ответы [ 4 ]

12 голосов
/ 06 марта 2009

Да, то, как вы их написали, эквивалентно.

Однако вам не нужно ждать завершения потока Superdaemon. Когда основной поток завершает выполнение main (), этот поток завершается, но JVM не будет. JVM будет работать до тех пор, пока последний не-демонический поток не выйдет из своего метода run.

Например,

public class KeepRunning {
  public static void main(String[] args) {
    Superdaemon d = new Superdaemon();
    d.start();
    System.out.println(Thread.currentThread().getName() + ": leaving main()");
  }
}

class Superdaemon extends Thread {
  public void run() {
    System.out.println(Thread.currentThread().getName() + ": starting");
    try { Thread.sleep(2000); } catch(InterruptedException e) {}
    System.out.println(Thread.currentThread().getName() + ": completing");
  }
}

Вы увидите вывод:

main: leaving main()
Thread-0: starting
Thread-0: completing

Другими словами, сначала завершается основной поток, затем завершается вспомогательный поток, и JVM завершается.

6 голосов
/ 06 марта 2009

Проблема в том, что такие книги, как JCIP, защищают нас от использования Executors для запуска Threads. Поэтому я стараюсь не использовать Thread.start (). Я не уверен, что обязательно выберу конкретный способ делать вещи, основываясь на простоте. Должна быть более убедительная причина, нет?

Убедительная причина использования java.util.concurrent заключается в том, что многопоточное программирование очень сложно. Java предлагает инструменты для этого (потоки, синхронизированные и volatile ключевые слова), но это не значит, что вы можете безопасно использовать их напрямую, не стреляя себе в ногу: либо слишком много синхронизация, приводящая к ненужным узким местам и тупикам, или слишком мала, что приводит к нестабильному поведению из-за условий гонки).

С java.util.concurrent вы получаете набор утилит (написанных экспертами) для наиболее распространенных шаблонов использования, которые вы можете просто использовать, не беспокоясь о том, что вы правильно поняли вещи низкого уровня.

В вашем конкретном случае, однако, я не совсем понимаю, зачем вам вообще нужен отдельный поток, вы могли бы также использовать основной:

public static void main( String[] args )
{
    Runnable t = new Superdaemon();
    t.run();
}

Исполнители предназначены для задач, которые вы хотите запускать в фоновом режиме (когда у вас есть несколько параллельных задач или когда ваш текущий поток может продолжать делать что-то еще).

1 голос
/ 06 марта 2009

Future.get () получит будущий ответ от асинхронного вызова. Это также заблокирует, если вызов еще не был завершен. Это очень похоже на объединение потоков.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/Future.html

0 голосов
/ 06 марта 2009

Sort'a. Future.get() предназначен для того, чтобы поток завершил и вычислил что-то, а затем вернул его в вызывающий поток безопасным способом. Это сработало бы, если бы get никогда не вернулся. Но я бы остановился на вызове join, так как он проще и не требует дополнительных ресурсов для Исполнителя (не так уж много).

Редактировать

Похоже, ExecutorService.submit(Runnable) предназначен для того, чтобы делать то, что вы пытаетесь. Он просто возвращает null, когда Runnable завершается. Интересно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...