developer tip

ExecutorService를 Java의 데몬으로 전환

copycodes 2020. 11. 13. 23:42
반응형

ExecutorService를 Java의 데몬으로 전환


Java 1.6에서 ExecutoreService를 사용하고 있습니다.

ExecutorService pool = Executors.newFixedThreadPool(THREADS). 

주 스레드가 완료되면 (스레드 풀에서 처리되는 모든 작업과 함께)이 풀은 명시 적으로 호출 할 때까지 프로그램이 종료되지 않도록합니다.

pool.shutdown();

어떻게 든이 풀에서 사용하는 내부 스레드 관리를 데몬 스레드로 전환하여 호출하지 않아도됩니까? 아니면 여기에 뭔가 빠졌습니까?


아마도 가장 간단하고 선호되는 솔루션은 Marco13의 답변 이므로 투표 차이 (내 답변은 몇 년 더 오래됨) 또는 수락 마크 (내 솔루션이 최선이 아니라 OP 상황에 적합하다는 것을 의미합니다)에 속지 마십시오.


ThreadFactoryExecutor 내부의 스레드를 데몬으로 설정 하는 사용할 수 있습니다 . 이것은 또한 데몬 스레드가되는 방식으로 실행기 서비스에 영향을 미치므로 다른 비 데몬 스레드가 없을 경우 해당 (및 그에 의해 처리되는 스레드)가 중지됩니다. 다음은 간단한 예입니다.

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()응용 프로그램이 완료 될 때 자동으로 메서드를 호출하는 실행기를 얻으 려면 실행기를 Guava의 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();
        }
    }
});

ExecutorService일정 기간 동안 활동이 없으면 모든 스레드를 종료하는를 생성하는 내장 기능이 이미 있습니다.을 생성 ThreadPoolExecutor하고 원하는 타이밍 정보를 전달한 다음 allowCoreThreadTimeout(true)이 실행기 서비스 를 호출 할 수 있습니다 .

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

편집 주석의 설명을 참조하십시오.이 스레드 풀 실행기는 응용 프로그램이 종료 될 때 자동으로 종료되지 않습니다. 실행기는 응용 프로그램이 종료 된 후에도 계속 실행되지만 keepAliveTime. 정확한 응용 프로그램 요구 사항에 따라 keepAliveTime몇 초 이상이어야하는 경우 Pshemo답변에 있는 솔루션 이 더 적절할 수 있습니다. 스레드가 데몬 스레드로 설정되면 응용 프로그램이 종료 될 때 즉시 종료됩니다. .


Guava의 ThreadFactoryBuilder 클래스를 사용합니다.

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

아직 Guava를 사용하고 있지 않다면 Pshemo의 답변 상단에 설명 된대로 ThreadFactory 하위 클래스를 사용 하겠습니다.


예.

You simply need to create your own ThreadFactory class that creates daemon threads rather than regular threads.


If you only want to use it in one place, then you can inline the java.util.concurrent.ThreadFactory implementation, e.g. for a pool with 4 threads you would write (example shown as a lambda assuming Java 1.8 or newer):

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

But I usually want all of my Thread factories to produce daemon threads, so I add a utility class as follows:

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

That allows me to easily pass DaemonThreadFactory.instance to the ExecutorService, e.g.

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

or use it to easily start a daemon Thread from a Runnable, e.g.

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

This solution is similar to @Marco13's but instead of creating our own ThreadPoolExecutor, we can modify the one returned by Executors#newFixedThreadPool(int nThreads). Here's how:

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

You can use Guava's ThreadFactoryBuilder. I didn't want to add the dependency and I wanted the functionality from Executors.DefaultThreadFactory, so I used composition:

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

If you have a known list of tasks, you don't need daemon threads at all. You can simply call shutdown() on the ExecutorService after submitting all your tasks.

When your main thread is complete, use the awaitTermination() method to allow time for the submitted tasks to complete.The currently submitted tasks will be executed, and the thread pool will terminate its control thread once they have been completed.

for (Runnable task : tasks) {
  threadPool.submit(task);
}
threadPool.shutdown();
/*... do other stuff ...*/
//All done, ready to exit
while (!threadPool.isTerminated()) {
  //this can throw InterruptedException, you'll need to decide how to deal with that.
  threadPool.awaitTermination(1,TimeUnit.SECOND); 
}

참고URL : https://stackoverflow.com/questions/13883293/turning-an-executorservice-to-daemon-in-java

반응형