web-dev-qa-db-ja.com

JavaでExecutorServiceをデーモンに変える

私はJava 1.6でExecutoreServiceを使用しています。

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

メインスレッドが(スレッドプールによって処理されるすべてのタスクと共に)終了すると、このプールにより、明示的に呼び出すまでプログラムがシャットダウンできなくなります。

pool.shutdown();

このプールで使用される内部スレッド管理を何らかの方法でデーモンスレッドに変えることで、これを呼び出す必要を回避できますか?または、ここで何かが欠けています。

64
Antiz

おそらく最も簡単で好ましい解決策は Marco13の回答 にあるので、投票の差(私の答えは数年古い)や承認マーク(それだけで鉱山ソリューションがOPの状況に適していたということではなく、最良であるということです)。


ThreadFactoryを使用して、Executor内のスレッドをデーモンに設定できます。これは、executorサービスに影響を与え、デーモンスレッドにもなるため、他の非デーモンスレッドがなくなると、サービス(およびそれによって処理されるスレッド)が停止します。以下に簡単な例を示します。

_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);
_

ただし、タスクを終了させ、同時にアプリケーションの完了時にshutdown()メソッドを自動的に呼び出すエクゼキューターを取得したい場合は、エグゼキューターを グアバの_MoreExecutors.getExitingExecutorService_ .

_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();
        }
    }
});
_
91
Pshemo

一定の非アクティブ期間後にすべてのスレッドを終了するExecutorServiceを作成するための組み込み機能が既にあります。ThreadPoolExecutorを作成し、必要なタイミング情報を渡し、allowCoreThreadTimeout(true)このexecutorサービスの場合:

/**
 * 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;
}

編集コメント内のコメントを参照してください。このスレッドプールエグゼキュータは、アプリケーションの終了時に自動的にシャットダウンしないことに注意してください。 executorは、アプリケーションの終了後も引き続き実行されますが、keepAliveTimeを超えることはありません。正確なアプリケーション要件に応じて、keepAliveTimeを数秒より長くする必要がある場合、 answer by Pshemo の解決策がより適切である可能性があります。デーモンスレッドである場合、アプリケーションが終了するとすぐに終了します。

42
Marco13

GuavaのThreadFactoryBuilderクラスを使用します。

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

まだGuavaを使用していない場合は、 Pshemo's answer の上部で説明したようなThreadFactoryサブクラスを使用します

19
Phil Hayward

はい。

通常のスレッドではなく、デーモンスレッドを作成する独自のThreadFactoryクラスを作成するだけです。

4
SLaks

1つの場所でのみ使用する場合は、Java.util.concurrent.ThreadFactory実装、例4つのスレッドを持つプールの場合(Java 1.8以降)を想定したラムダとして示される例:

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

ただし、通常はすべてのスレッドファクトリでデーモンスレッドを生成するため、次のようにユーティリティクラスを追加します。

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;
    }
}

これにより、DaemonThreadFactory.instanceExecutorServiceに、たとえば.

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

または、Runnableからdaemonスレッドを簡単に開始するために使用します。

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

GuavaのThreadFactoryBuilderを使用できます。依存関係を追加したくなかったので、Executors.DefaultThreadFactoryの機能が必要だったため、構成を使用しました。

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

このソリューションは@ Marco13に似ていますが、独自のThreadPoolExecutorを作成する代わりに、Executors#newFixedThreadPool(int nThreads)によって返されるものを変更できます。方法は次のとおりです。

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