it-swarm.com.de

Einen ExecutorService zu einem Dämon in Java machen

Ich verwende einen ExecutoreService in Java 1.6, der einfach von gestartet wird 

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

Wenn mein Haupt-Thread abgeschlossen ist (zusammen mit allen vom Thread-Pool verarbeiteten Aufgaben), verhindert dieser Pool, dass mein Programm heruntergefahren wird, bis ich es explizit aufrufe

pool.shutdown();

Kann ich es vermeiden, dies aufrufen zu müssen, indem die interne Threadverwaltung, die von diesem Pool verwendet wird, in einen Deamon-Thread umgewandelt wird? Oder fehlt mir hier etwas?.

58
Antiz

Die wahrscheinlich einfachste und bevorzugte Lösung ist in Marco13s Antwort Lassen Sie sich also nicht durch Stimmabweichungen (meine Antwort ist einige Jahre älter) oder Akzeptanzzeichen täuschen (dies bedeutet nur, dass meine Lösung für OP-Umstände geeignet war und nicht das Beste ist.) ).


Sie können ThreadFactory verwenden, um Threads in Executor auf Daemons festzulegen. Dies wirkt sich auf den Executor-Service so aus, dass er auch zu einem Dämon-Thread wird, so dass er (und von ihm behandelte Threads) angehalten werden, wenn es keinen anderen Nicht-Daemon-Thread gibt. Hier ist ein einfaches Beispiel:

ExecutorService exec = Executors.newFixedThreadPool(4,
        new ThreadFactory() {
            public Thread newThread(Runnable r) {
                Thread t = Executors.defaultThreadFactory().newThread(r);
                t.setDaemon(true);
                return t;
            }
        });

exec.execute(YourTaskNowWillBeDaemon);

Wenn Sie jedoch einen Executor erhalten möchten, der seine Aufgabe beenden lässt, und gleichzeitig die shutdown()-Methode automatisch aufrufen wird, wenn die Anwendung abgeschlossen ist, möchten Sie Ihren Executor möglicherweise mit GuavasMoreExecutors.getExitingExecutorService umschließen.

ExecutorService exec = MoreExecutors.getExitingExecutorService(
        (ThreadPoolExecutor) Executors.newFixedThreadPool(4), 
        100_000, TimeUnit.DAYS//period after which executor will be automatically closed
                             //I assume that 100_000 days is enough to simulate infinity
);
//exec.execute(YourTask);
exec.execute(() -> {
    for (int i = 0; i < 3; i++) {
        System.out.println("daemon");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});
85
Pshemo

Es gibt bereits eine integrierte Funktion zum Erstellen eines ExecutorService, die alle Threads nach einer bestimmten Inaktivitätsphase beendet: Sie können ein ThreadPoolExecutor erstellen, ihm die gewünschten Timing-Informationen übergeben und dann allowCoreThreadTimeout(true) für diesen Executor-Service aufrufen:

/**
 * Creates an executor service with a fixed pool size, that will time 
 * out after a certain period of inactivity.
 * 
 * @param poolSize The core- and maximum pool size
 * @param keepAliveTime The keep alive time
 * @param timeUnit The time unit
 * @return The executor service
 */
public static ExecutorService createFixedTimeoutExecutorService(
    int poolSize, long keepAliveTime, TimeUnit timeUnit)
{
    ThreadPoolExecutor e = 
        new ThreadPoolExecutor(poolSize, poolSize,
            keepAliveTime, timeUnit, new LinkedBlockingQueue<Runnable>());
    e.allowCoreThreadTimeOut(true);
    return e;
}

BEARBEITEN Beachten Sie die Anmerkungen in den Kommentaren: Beachten Sie, dass dieser Threadpool-Executor nicht automatisch heruntergefahren wird, wenn die Anwendung beendet wird. Der Executor wird nach dem Beenden der Anwendung weiter ausgeführt, jedoch nicht länger als keepAliveTime. Wenn keepAliveTime abhängig von den genauen Anwendungsanforderungen länger als einige Sekunden sein muss, ist die Lösung in der answer von Pshemo möglicherweise geeigneter: Wenn die Threads als Daemon-Threads festgelegt sind, werden sie beendet sofort, wenn die Anwendung beendet wird.

37
Marco13

Ich würde Guavas ThreadFactoryBuilder-Klasse verwenden.

ExecutorService threadPool = Executors.newFixedThreadPool(THREADS, new ThreadFactoryBuilder().setDaemon(true).build());

Wenn Sie Guava noch nicht verwenden, würde ich eine ThreadFactory-Unterklasse verwenden, wie sie oben in Pshemos Antwort beschrieben ist.

16
Phil Hayward

Ja.

Sie müssen lediglich Ihre eigene ThreadFactory-Klasse erstellen, die Daemon-Threads anstelle von regulären Threads erstellt.

4
SLaks

Wenn Sie es nur an einer Stelle verwenden möchten, können Sie die Java.util.concurrent.ThreadFactory-Implementierung (z. B. als Lambda unter der Annahme von Java 1.8 oder neuer) inline einfügen, z. für einen Pool mit 4 Threads würden Sie schreiben:

ExecutorService pool = Executors.newFixedThreadPool(4,
            (Runnable r) -> {
                Thread t = new Thread(r);
                t.setDaemon(true);
                return t;
            });

Normalerweise möchte ich jedoch, dass alle meine Thread-Fabriken Daemon-Threads erstellen. Daher füge ich eine Dienstprogrammklasse wie folgt hinzu:

import Java.util.concurrent.ThreadFactory;

public class DaemonThreadFactory implements ThreadFactory {

    public final static ThreadFactory instance = 
                    new DaemonThreadFactory();

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}

Dadurch kann ich DaemonThreadFactory.instance leicht an die ExecutorService übergeben, z.

ExecutorService pool = Executors.newFixedThreadPool(
    4, DaemonThreadFactory.instance
);

oder verwenden Sie es, um einen daemon Thread einfach von einer Runnable aus zu starten, z.

DaemonThreadFactory.instance.newThread(
    () -> { doSomething(); }
).start();
1
isapir

Sie können den ThreadFactoryBuilder von Guava verwenden. Ich wollte die Abhängigkeit nicht hinzufügen und wollte die Funktionalität von Executors.DefaultThreadFactory, also habe ich Komposition verwendet:

class DaemonThreadFactory implements ThreadFactory {
    final ThreadFactory delegate;

    DaemonThreadFactory() {
        this(Executors.defaultThreadFactory());
    }

    DaemonThreadFactory(ThreadFactory delegate) {
        this.delegate = delegate;
    }

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = delegate.newThread(r);
        thread.setDaemon(true);
        return thread;
    }
}
0
etherous

Diese Lösung ähnelt der von @ Marco13, aber anstatt unsere eigene ThreadPoolExecutor zu erstellen, können wir die von Executors#newFixedThreadPool(int nThreads) zurückgegebene ändern. Hier ist wie:

ExecutorService ex = Executors.newFixedThreadPool(nThreads);
 if(ex instanceof ThreadPoolExecutor){
    ThreadPoolExecutor tp = (ThreadPoolExecutor) ex;
    tp.setKeepAliveTime(time, timeUnit);
    tp.allowCoreThreadTimeOut(true);
}
0
Steve