Warum ExecutorService für lang laufende Threads verwenden?

8

Ich möchte ein Objekt, das einen Daemon-Thread erzeugt, der während der gesamten Lebensdauer des Prozesses ausgeführt wird. Nehmen wir nur an, es handelt sich um einen Thread in einem eingebetteten System, der darauf wartet, Befehle an einem Diagnoseport zu empfangen und zu verarbeiten. Aber es könnte wirklich alles sein. Die Hauptidee ist, dass es etwas über einen langen Zeitraum beobachtet ; Es führt keine Abfolge von Aufgaben aus .

Allgemeine Java-Weisheit sagt: Niemals instanziieren Thread, ExecutorServicestattdessen eine verwenden. (Siehe z. B. diese Antwort. ) Aber was ist der Vorteil? Die Verwendung eines Thread- Pools als Mittel zum Erstellen eines einzelnen Threads mit langer Laufzeit erscheint sinnlos. Wie wäre es besser, als wenn ich das schreiben würde?

class Foobar {
    public Foobar() {
        this.threadFactory = Executors.defaultThreadFactory();
        ...
    }

    public Foobar(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        ...
    }

    public void start() {
        fooThread = threadFactory.newThread(new Runnable() { ... });
        fooThread.setDaemon(true);
        fooThread.start();
    }
    ...
}

Hinweis: Diese Frage scheint meiner ähnlich zu sein, aber die Antworten sagen nur, wie ein Thread-Pool verwendet wird, nicht warum .

Solomon Slow
quelle
Nicht sicher über Java, aber andere Sprachen empfehlen normalerweise ausdrücklich, einen Thread-Pool für einen lang laufenden Thread zu vermeiden. Wie Sie sagten, ist es nicht sinnvoll, eine begrenzte Anzahl von Ressourcen über einen längeren Zeitraum zu verwenden.
Frank Hileman

Antworten:

11

Ich denke, dass "Nie" ein zu starkes Wort ist. Es ist eher die zweite Regel der Optimierung: "Noch nicht".

Meiner Meinung nach besteht der Hauptgrund für die Verwendung eines Executor-Dienstes darin, die Anzahl der ausgeführten Threads zu verwalten. Wenn Sie beliebigen Klassen erlauben, ihre eigenen Threads zu erstellen, finden Sie möglicherweise schnell 1.000 CPU-gebundene (oder ständig kontextumschaltende) Threads. Ein Executor-Service löst dieses Problem, indem er Einschränkungen für die Thread-Erstellung bereitstellt.

Ein zweiter Grund ist, dass das richtige Starten eines Threads einige Überlegungen erfordert:

  • Daemon oder Nicht-Daemon? Wenn Sie dies falsch verstehen, müssen Sie aufrufen System.exit(), um Ihr Programm herunterzufahren.
  • Welche Priorität? Viele Swing-Programmierer dachten, sie seien schlau, indem sie Hintergrund-Threads für CPU-intensive Aufgaben hochfuhren, erkannten jedoch nicht, dass der Event-Dispatch-Thread mit der höchsten Priorität ausgeführt wird und untergeordnete Threads die Priorität ihrer Eltern erben.
  • Wie gehe ich mit nicht erfassten Ausnahmen um?
  • Was ist ein passender Name? Scheint albern, von einem Namen besessen zu sein, aber zweckgebundene Threads geben Ihnen viel Hilfe beim Debuggen.

Das heißt, es gibt sicherlich Fälle, in denen es angebracht ist, Threads hochzufahren, anstatt sich auf einen Thread-Pool zu verlassen.

  • Ich würde den Thread-Pool in dem Fall verwenden, in dem meine Anwendung Aufgaben generiert, und ich möchte steuern, wie diese Aufgaben ausgeführt werden.
  • Ich würde dedizierte Threads erstellen, bei denen ein oder mehrere Threads für die Verarbeitung von extern generierten Ereignissen vorgesehen sind.

"Extern generiert" ist kontextabhängig. Der klassische Fall ist der Thread am Ende einer Nachrichtenwarteschlange oder eines Sockets, der diese Warteschlange abfragen und eine Aktion ausführen muss (was das Versenden einer Aufgabe an einen Pool beinhalten kann).

Es könnte sich jedoch auch auf Ereignisse beziehen, die außerhalb eines bestimmten Moduls generiert wurden: Ich halte es beispielsweise für durchaus vernünftig, dass Log4J AsyncAppendereinen eigenen Thread hochfährt, anstatt zu erwarten, dass die Anwendung einen Pool bereitstellt.

kdgregory
quelle