ExecutorService의 이름 지정 스레드 및 스레드 풀


228

Executor프레임 워크 를 활용하는 응용 프로그램이 있다고 가정 해 보겠습니다.

Executors.newSingleThreadExecutor().submit(new Runnable(){
    @Override
    public void run(){
        // do stuff
    }
}

이 응용 프로그램을 디버거에서 실행하면 다음과 같은 (기본) 이름으로 스레드가 생성됩니다 Thread[pool-1-thread-1]. 보시다시피, 이것은별로 유용 Executor하지 않으며 내가 알 수있는 한 프레임 워크는 작성된 스레드 또는 스레드 풀의 이름을 쉽게 지정할 수 있는 방법을 제공하지 않습니다.

그렇다면 스레드 / 스레드 풀의 이름을 어떻게 제공합니까? 예를 들어 Thread[FooPool-FooThread].

답변:


118

를 제공 할 ThreadFactorynewSingleThreadScheduledExecutor(ThreadFactory threadFactory)있습니다. 팩토리는 스레드 작성에 대한 책임이 있으며 이름을 지정할 수 있습니다.

Javadoc 을 인용하려면 :

새 스레드 만들기

를 사용하여 새 스레드가 생성됩니다 ThreadFactory. 다르게 지정되지 않으면, Executors.defaultThreadFactory()스레드가 ThreadGroup동일한 NORM_PRIORITY우선 순위 및 비 데몬 상태에 있도록 스레드를 작성 하는 a 가 사용됩니다 . 다른을 제공 ThreadFactory하면 스레드의 이름, 스레드 그룹, 우선 순위, 데몬 상태 등을 변경할 수 있습니다. ThreadFactory에서 from을 반환하여 요청시 스레드를 작성하지 못하면 newThread실행 프로그램이 계속되지만 태스크를 실행할 수 없습니다.


283

구아바에는 거의 항상 필요한 것이 있습니다 .

ThreadFactory namedThreadFactory = 
  new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()

그리고 당신에게 전달하십시오 ExecutorService.


3
환상적입니다!
Martin Vseticka

25
그거 슬프다! :-(
exic

어디에서 "구아바"를 찾을 수 있는지 잘 모르겠습니다. 구글의 구아바에는 많은 부분이 있으며 같은 이름을 가진 수십 개의 라이브러리가 있습니다. search.maven.org/artifact/com.google.guava/guava/29.0-jre/… 을 의미한다고 가정합니다 . 맞습니까? 제공하는 링크는 Google에서 제공 한 것으로 제안하지만 Google은 Magua / Sonatype에 "guava"라는 약 6 개의 아티팩트도 있습니다.
Jason

@Jason-사소한 Java 프로젝트를 작성하는 경우 구아바가 이미 종속성으로 있어야합니다. 그리고 여기 있습니다 : github.com/google/guava
pathikrit

@pathikrit, 감사합니다! 구아바에서 더 공부해야한다고 생각합니다 :-)
Jason

95

고유 한 스레드 팩토리를 제공하여 적절한 이름의 스레드를 작성할 수 있습니다. 예를 들면 다음과 같습니다.

class YourThreadFactory implements ThreadFactory {
   public Thread newThread(Runnable r) {
     return new Thread(r, "Your name");
   }
 }

Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);

58

스레드가 실행되는 동안 나중에 스레드 이름을 변경할 수도 있습니다.

Thread.currentThread().setName("FooName");

예를 들어 다른 유형의 작업에 동일한 ThreadFactory를 사용하는 경우 관심이 될 수 있습니다.


7
FlorianT가 설명했듯이 많은 다른 유형의 스레드가 있으며 이름을 위해 여러 개의 ThreadFactory 객체를 만들지 않기 때문에 훌륭하게 작동했습니다. Thread.currentThread (). setName ( "FooName"); 각 run () 메서드의 첫 번째 줄로.
Robin Zimmermann

5
이것에 대한 하나의 사소한 문제는 문서에 설명 된 실패 동작이 발생할 때 (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.)입니다. ExecutorService가 스레드를 교체하면 ThreadFactory에 의해 이름이 지정됩니다. 그런 다음 디버깅하는 동안 유용한 이름이 될 수 있지만 이름이 사라지는 것을 다시 볼 수 있습니다.
sethro

단순히 훌륭합니다! 감사합니다.
ass

1
다른 답변에서 알 수 있듯이 이것은 이름을 설정하는 빠르고 더러운 방법이며 여러 스레드로 그렇게하면 모두 같은 이름을 갖습니다!
타노

종료시 스레드 이름을 다시 원래 이름으로 설정하려고 할 수 있습니다. 다른 관련없는 작업을 수행하는 경우에도 이름이 유지 될 수 있기 때문입니다.
더스틴 K

51

BasicThreadFactory아파치 평민 - 랭에서 또한 명명 동작을 제공하는 데 유용합니다. 익명의 내부 클래스를 작성하는 대신 Builder를 사용하여 원하는대로 스레드 이름을 지정할 수 있습니다. 다음은 javadocs의 예입니다.

 // Create a factory that produces daemon threads with a naming pattern and
 // a priority
 BasicThreadFactory factory = new BasicThreadFactory.Builder()
     .namingPattern("workerthread-%d")
     .daemon(true)
     .priority(Thread.MAX_PRIORITY)
     .build();
 // Create an executor service for single-threaded execution
 ExecutorService exec = Executors.newSingleThreadExecutor(factory);

30

Spring을 사용 CustomizableThreadFactory하는 경우 스레드 이름 접두사를 설정할 수 있습니다.

예:

ExecutorService alphaExecutor =
    Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));

또는 -를 ExecutorService사용하여 Spring bean으로 만들 수 있으며 ThreadPoolExecutorFactoryBean스레드는 모두 beanName-접두어 로 이름이 지정됩니다 .

@Bean
public ThreadPoolExecutorFactoryBean myExecutor() {
    ThreadPoolExecutorFactoryBean executorFactoryBean = new ThreadPoolExecutorFactoryBean();
    // configuration of your choice
    return executorFactoryBean;
}

위의 예에서 스레드 이름은 myExecutor-접두사 로 지정됩니다 . 당신은 다른 값 (예.에 명시 적으로 접두사를 설정할 수 있습니다 "myPool-"설정하여) executorFactoryBean.setThreadNamePrefix("myPool-")공장 콩에.


CustomizableThreadFactory를 찾을 수 없습니까? jdk 1.7을 사용하고 있습니다. 내가 여기서 무엇을 놓치고 있는지 아는가?
Kamran Shahid

@ KamranShahid 이것은 스프링 프레임 워크 클래스입니다, 당신은 그것을 가지고 봄을 사용해야합니다
Adam Michalik

20

Oracle에는이를 위한 공개 RFE 가 있습니다 . 오라클 직원의 의견으로는 문제를 이해하지 못하고 해결되지 않는 것 같습니다. JDK에서 지원하기가 간단하지만 (이전 버전과의 호환성을 유지하지 않고) RFE가 오해하는 것은 부끄러운 일입니다.

지적했듯이 자신의 ThreadFactory 를 구현해야합니다 . 이 목적으로 Guava 또는 Apache Commons를 가져 오지 않으려면 여기에 ThreadFactory사용할 수 있는 구현을 제공하십시오 . 스레드 이름 접두어를 "pool"이외의 것으로 설정하는 기능을 제외하고는 JDK에서 얻는 것과 정확히 유사합니다.

package org.demo.concurrency;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ThreadFactory with the ability to set the thread name prefix. 
 * This class is exactly similar to 
 * {@link java.util.concurrent.Executors#defaultThreadFactory()}
 * from JDK8, except for the thread naming feature.
 *
 * <p>
 * The factory creates threads that have names on the form
 * <i>prefix-N-thread-M</i>, where <i>prefix</i>
 * is a string provided in the constructor, <i>N</i> is the sequence number of
 * this factory, and <i>M</i> is the sequence number of the thread created 
 * by this factory.
 */
public class ThreadFactoryWithNamePrefix implements ThreadFactory {

    // Note:  The source code for this class was based entirely on 
    // Executors.DefaultThreadFactory class from the JDK8 source.
    // The only change made is the ability to configure the thread
    // name prefix.


    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    /**
     * Creates a new ThreadFactory where threads are created with a name prefix
     * of <code>prefix</code>.
     *
     * @param prefix Thread name prefix. Never use a value of "pool" as in that
     *      case you might as well have used
     *      {@link java.util.concurrent.Executors#defaultThreadFactory()}.
     */
    public ThreadFactoryWithNamePrefix(String prefix) {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup()
                : Thread.currentThread().getThreadGroup();
        namePrefix = prefix + "-"
                + poolNumber.getAndIncrement()
                + "-thread-";
    }


    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                namePrefix + threadNumber.getAndIncrement(),
                0);
        if (t.isDaemon()) {
            t.setDaemon(false);
        }
        if (t.getPriority() != Thread.NORM_PRIORITY) {
            t.setPriority(Thread.NORM_PRIORITY);
        }
        return t;
    }
}

당신이 그것을 사용하고 싶을 때, 당신은 모든 Executors방법이 당신 자신을 제공 할 수 있다는 사실을 이용합니다 ThreadFactory.

    Executors.newSingleThreadExecutor();

쓰레드가 명명 된 곳에서 ExecutorService를 제공 pool-N-thread-M하지만

    Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc"));

당신은 스레드의 이름을 지정하는 ExecutorService를 얻을 수 있습니다 primecalc-N-thread-M. 짜잔!


마지막 스 니펫에서 닫는 괄호를 놓쳤습니다
k.liakos

SonarLint / 큐브 (Qube)를 사용하지 선호 것을 그냥 빨리 노트 ThreadGroup에 찬성 ThreadPoolExecutor.
Drakes

8
private class TaskThreadFactory implements ThreadFactory
{

    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r, "TASK_EXECUTION_THREAD");

        return t;
    }

}

ThreadFactory를 executorservice에 전달하면 좋습니다.


8

빠르고 더러운 방법은 방법에 사용 Thread.currentThread().setName(myName);하는 run()것입니다.


7

ThreadFactory 확장

public interface ThreadFactory

요청시 새 스레드를 작성하는 오브젝트입니다. 스레드 팩토리를 사용하면 새 스레드에 대한 호출의 배선이 제거되어 응용 프로그램에서 특수 스레드 하위 클래스, 우선 순위 등을 사용할 수 있습니다.

Thread newThread(Runnable r)

새로운 Thread를 구축합니다. 구현은 우선 순위, 이름, 데몬 상태, ThreadGroup 등을 초기화 할 수도 있습니다.

샘플 코드 :

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;

class SimpleThreadFactory implements ThreadFactory {
   String name;
   AtomicInteger threadNo = new AtomicInteger(0);

   public SimpleThreadFactory (String name){
       this.name = name;
   }
   public Thread newThread(Runnable r) {
     String threadName = name+":"+threadNo.incrementAndGet();
     System.out.println("threadName:"+threadName);
     return new Thread(r,threadName );
   }
   public static void main(String args[]){
        SimpleThreadFactory factory = new SimpleThreadFactory("Factory Thread");
        ThreadPoolExecutor executor= new ThreadPoolExecutor(1,1,60,
                    TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardPolicy());


        final ExecutorService executorService = Executors.newFixedThreadPool(5,factory);

        for ( int i=0; i < 100; i++){
            executorService.submit(new Runnable(){
                 public void run(){
                    System.out.println("Thread Name in Runnable:"+Thread.currentThread().getName());
                 }
            });
        }
        executorService.shutdown();
    }
 }

산출:

java SimpleThreadFactory

thread no:1
thread no:2
Thread Name in Runnable:Factory Thread:1
Thread Name in Runnable:Factory Thread:2
thread no:3
thread no:4
Thread Name in Runnable:Factory Thread:3
Thread Name in Runnable:Factory Thread:4
thread no:5
Thread Name in Runnable:Factory Thread:5

....기타


1
스레드 카운터는 스레드로부터 안전하지 않습니다. AtomicInteger를 사용해야합니다.
피노

제안 해 주셔서 감사합니다. 나는 당신의 제안을 통합했습니다.
Ravindra babu

5

다른 답변에서 이미 언급했듯이 java.util.concurrent.ThreadFactory인터페이스 구현을 직접 만들고 사용할 수 있습니다 (외부 라이브러리 필요 없음). String.format메소드를 사용 하고 생성자의 인수로 스레드의 기본 이름을 사용하므로 이전 답변과 다르기 때문에 아래 코드를 붙여 넣습니다 .

import java.util.concurrent.ThreadFactory;

public class NameableThreadFactory implements ThreadFactory{
    private int threadsNum;
    private final String namePattern;

    public NameableThreadFactory(String baseName){
        namePattern = baseName + "-%d";
    }

    @Override
    public Thread newThread(Runnable runnable){
        threadsNum++;
        return new Thread(runnable, String.format(namePattern, threadsNum));
    }    
}

그리고 이것은 사용법의 예입니다.

ThreadFactory  threadFactory = new NameableThreadFactory("listenerThread");        
final ExecutorService executorService = Executors.newFixedThreadPool(5, threadFactory);

편집 : 내 ThreadFactory구현을 스레드 안전으로 설정하십시오. @ mchernyakov 덕분 에 지적했습니다. 문서의 어느
곳에서도 ThreadFactory구현이 스레드 안전해야한다고 말하지는 않지만 스레드 안전하다는 사실 DefaultThreadFactory은 큰 힌트입니다.

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class NameableThreadFactory implements ThreadFactory{
    private final AtomicInteger threadsNum = new AtomicInteger();

    private final String namePattern;

    public NameableThreadFactory(String baseName){
        namePattern = baseName + "-%d";
    }

    @Override
    public Thread newThread(Runnable runnable){
        return new Thread(runnable, String.format(namePattern, threadsNum.addAndGet(1)));
    }    
}

1
스레드 카운터 (threadsNum)는 스레드 안전하지 않으므로 AtomicInteger를 사용해야합니다.
mchernyakov

그것을 지적 해 주셔서 감사합니다, @mchernyakov 방금 답변을 편집했습니다.
Víctor Gil

4

기존 팩토리를 장식하는 데 사용하는 자체 개발 핵심 Java 솔루션 :

public class ThreadFactoryNameDecorator implements ThreadFactory {
    private final ThreadFactory defaultThreadFactory;
    private final String suffix;

    public ThreadFactoryNameDecorator(String suffix) {
        this(Executors.defaultThreadFactory(), suffix);
    }

    public ThreadFactoryNameDecorator(ThreadFactory threadFactory, String suffix) {
        this.defaultThreadFactory = threadFactory;
        this.suffix = suffix;
    }

    @Override
    public Thread newThread(Runnable task) {
        Thread thread = defaultThreadFactory.newThread(task);
        thread.setName(thread.getName() + "-" + suffix);
        return thread;
    }
}

실제로 :

Executors.newSingleThreadExecutor(new ThreadFactoryNameDecorator("foo"));

3
Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());

Runnable getJob() {
        return () -> {
            // your job
        };
}

3

예를 들어 defaultThreadFactory와 같은 일부 기존 구현을 사용하여 고유 한 ThreadFactory 구현을 작성하고 끝에 이름을 변경할 수 있습니다.

ThreadFactory를 구현하는 예 :

class ThreadFactoryWithCustomName implements ThreadFactory {
    private final ThreadFactory threadFactory;
    private final String name;

    public ThreadFactoryWithCustomName(final ThreadFactory threadFactory, final String name) {
        this.threadFactory = threadFactory;
        this.name = name;
    }

    @Override
    public Thread newThread(final Runnable r) {
        final Thread thread = threadFactory.newThread(r);
        thread.setName(name);
        return thread;
    }
}

그리고 사용법 :

Executors.newSingleThreadExecutor(new ThreadFactoryWithCustomName(
        Executors.defaultThreadFactory(),
        "customName")
    );

3

아래처럼 똑같이 사용합니다 ( guava라이브러리 필요 ).

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);

1
가치는 주목할 ThreadFactoryBuilder구글 구아바 라이브러리입니다.
Craig Otis

3

단일 스레드 실행기의 이름을 변경하려는 경우 람다를 스레드 팩토리로 사용하는 것이 가장 쉽다는 것을 알았습니다.

Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));

이것은 두 개의 스레드를 만듭니다. 한 명 "당신의 이름"다른 "풀-N-스레드-M"
Systemsplanet

@Systemsplanet 아니요, 그렇지 않습니다. 실행기를 사용하여 잠자는 스레드를 실행하는 최소 예제에서 스레드 덤프를 수행하면 다음 스레드가 표시됩니다.main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
CamW

흠, 내가 시도했을 때 그랬어. 새로운 Runnable ()을 전달하면 스레드를 생성하고 스레드를 직접 생성하기 때문에 그렇게하는 것이 합리적입니다.
행성

ThreadPoolExecutor를 대신 사용했거나 다른 목적으로 실행 중이기를 기대합니다. 이 코드는 "pool-N-thread-M"스레드를 만들지 않습니다. 또한, 나는 그것이 의미가 있다고 믿지 않습니다. "새 Runnable ()을 전달하면 스레드가 생성됩니다"라는 문장이 올바르지 않습니다. 실행 가능한 스레드를 사용하여 스레드를 작성하고 단일 스레드 실행 프로그램이므로 한 번 수행합니다. 하나의 스레드 만 작성됩니다.
CamW

2

이것은 스레드 덤프 분석기의 사용자 정의 이름을 제공하는 사용자 정의 된 공장입니다. 일반적으로 tf=nullJVM 기본 스레드 팩토리를 재사용합니다. 이 웹 사이트에는 고급 스레드 팩토리가 있습니다.

public class SimpleThreadFactory implements ThreadFactory {
    private ThreadFactory tf;
    private String nameSuffix;

    public SimpleThreadFactory (ThreadFactory tf, String nameSuffix) {
        this.tf = tf!=null ? tf : Executors.defaultThreadFactory();
        this.nameSuffix = nameSuffix; 
    }

    @Override public Thread newThread(Runnable task) {
        // default "pool-1-thread-1" to "pool-1-thread-1-myapp-MagicTask"
        Thread thread=tf.newThread(task);
        thread.setName(thread.getName()+"-"+nameSuffix);
        return thread;
    }
}

- - - - - 

ExecutorService es = Executors.newFixedThreadPool(4, new SimpleThreadFactory(null, "myapp-MagicTask") );

편의상 디버그 목적의 스레드 덤프 루프입니다.

    ThreadMXBean mxBean=ManagementFactory.getThreadMXBean();
    long[] tids = mxBean.getAllThreadIds();
    System.out.println("------------");
    System.out.println("ThreadCount="+tids.length);
    for(long tid : tids) {
        ThreadInfo mxInfo=mxBean.getThreadInfo(tid);
        if (mxInfo==null) {
            System.out.printf("%d %s\n", tid, "Thread not found");
        } else {
            System.out.printf("%d %s, state=%s, suspended=%d, lockowner=%d %s\n"
                    , mxInfo.getThreadId(), mxInfo.getThreadName()
                    , mxInfo.getThreadState().toString()
                    , mxInfo.isSuspended()?1:0
                    , mxInfo.getLockOwnerId(), mxInfo.getLockOwnerName()
            );
        }
    }

이것은 나를 위해 정말 잘 작동했습니다. 어느 쪽이든 건배.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.