Threads oder ThreadPool? Fester oder dynamischer ThreadPool?

8

Ich habe ein Java-Programm, das einen Port für die Eingabe abhört. Basierend auf Input ruft es einen Webservice auf und gibt dann einen Erfolg / Misserfolg an das Client-Programm zurück.

Ich gable einen Thread für jede Client-Verbindung. Die Antwort an den Client, der eine Verbindung zum Programm herstellt, muss schnell erfolgen.

Dies sind die Entscheidungen, die ich erwäge

  1. Verwenden Sie normale Threads
  2. verwenden ExecutorServicemitnewFixedThreadPool
  3. verwenden ExecutorServicemitnewCachedThreadPool

Der Grund, warum ich über Pools nachdenke, ist, dass meine Threads nur von kurzer Dauer sind - sie rufen einfach einen Webservice auf, geben das Ergebnis an den Client zurück und schließen die Verbindung.

Ich denke nicht, newFixedThreadPooldass das das Richtige wäre, denn dann würden Verbindungen in Warteschlangen warten, um einen Thread zu erhalten.

newCachedThreadPoolwäre bis auf eine Sache perfekt gewesen - Fäden sterben nach einer Minute. In meinem Fall bekomme ich Bursts von Verbindungen - dh mehrere Verbindungen, und dann kann es für einige Minuten zu einer Pause kommen und dann erneut zu Bursts. Ich denke, die Threads im CachedThreadPool würden sterben und müssten erneut erstellt werden - in diesem Fall könnte es manchmal wie # 1 funktionieren.

Idealerweise hätte ich gerne newCachedThreadPoolein Minimum gehabt - dh eine Einstellung, die besagt, dass die Anzahl der Threads niemals unter 20 fallen würde. So werden inaktive Threads getötet, aber niemals unter einen Mindestschwellenwert fallen gelassen.

Gibt es so etwas? Oder gibt es bessere Alternativen?

user93353
quelle
Schauen Sie sich QuickServer.org anstelle des eigenen Socket-Servers an. Es macht was Sie wollen -> Socket und Threads und hat viele Beispiele. kann es in 30 Minuten zum Laufen bringen. Getestet und stabil, habe es in 3 Projekten verwendet. "Open Source Java Bibliothek / Framework für die schnelle Erstellung robuster Multi-Client-TCP-Server-Anwendungen"
tgkprog

Antworten:

8

Die Methoden in der Executors-Klasse sind nur praktische Methoden für allgemeine Anwendungsfälle. Es stehen viel mehr Optionen zum Erstellen von Thread-Pools zur Verfügung.

So erstellen Sie dasselbe wie Executors.newCachedThreadPool (), jedoch mit mindestens 20 Threads (dies wird aus der Methode in Executors kopiert, wobei die Kern-Thread-Größe von 0 auf 20 geändert wird).

        return new ThreadPoolExecutor(20, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
Michael Krussel
quelle
4

Interessante Frage.

Ich würde dagegen empfehlen newCachedThreadPool. Es werden so viele Threads wie nötig ohne Obergrenze erzeugt. Es ist schlecht!

Der von Michael vorgeschlagene Ansatz scheint gut zu sein. Verwenden ThreadPoolExecutorund verwenden Sie eine Nummer, mit der Sie vertraut sind, als Kernpoolgröße. Legen Sie die maximale Anzahl von Threads auf etwas fest, das Sie tolerieren möchten (oder das Ihr Server verarbeiten kann).

Bitte beachten Sie, dass Sie so gut wie nichts mehr tun können, wenn Sie die Ressource erschöpft haben (die Warteschlange ist voll und die maximale Anzahl von Threads funktioniert). In diesem Fall können Sie eines der beiden Dinge tun: neue Verbindungen trennen oder Gegendruck ausüben (keine neue Arbeit durch Blockieren annehmen). TPE wird standardmäßig ausgelöst, RejectedExecutionExceptionwenn es voll ist, aber es ist einfach, das Blockierungsverhalten zu implementieren. Tatsächlich ist hier eine Open Source-Implementierung von BlockingThreadPoolExecutor .

Goblinjuice
quelle
2

In Bezug auf die RejectedExecutionException:

Anstelle eines BlockingThreadPoolExecutor können wir einfach den RejectedExecutionHandler im ThreadPoolExecutor so einstellen, dass der CallerRunPolicy- Handler verwendet wird.

Silmarx
quelle
0

Ich denke, die Verwendung eines Wrappers über einen ThreadPoolExecutor ist ein guter Weg. Ein Wrapper, mit dem Sie eine Initialisierung (wie einen Poolnamen, die Anzahl der Threads usw.) und eine Methode zum Hinzufügen einer Aufgabe (ausführbar) zu einem Pool, der zuvor mit einer Ihrer Init-Methoden erstellt wurde, verfügbar machen können.

Der Wrapper kann ändern, welchen Pool er verwendet, ohne anderen Code zu beeinflussen, und andere Methoden wie die Anzahl der Threads und Audits (vor / nach der Aufgabe) auf konsistente Weise verfügbar machen

Sie können auch eine eigene Thread-Factory für Ihren Pool implementieren, die einen benutzerdefinierten Namen, eine benutzerdefinierte Priorität und zumindest einen UncaughtExceptionHandler haben kann, damit Sie alle Fehler protokollieren können.

In meinem Pool Wrapper haben wir Methoden wie

public static boolean queqeAdd(String poolName, int coreSize, int maxSize, boolean usePriorityQ, String threadNamePrefix, int timeOutSeconds) {...}
public static void execute(String poolName, Runnable command, Integer priority){...}
public static long tasksCount(String poolName) 
public static long tasksCompletedCount(String poolName) 
public static int clearPoolRequest(String poolName, boolean intrpt) 
public static int shutdownPoolRequest(String poolName) 
public static void executeAll(String poolName, List<Runnable> lst) 
tgkprog
quelle