ExecutorServiceのスレッドとスレッドプールの命名


228

そのExecutorようなフレームワークを利用するアプリケーションがあるとしましょう

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

このアプリケーションをデバッガーで実行すると、次の(デフォルト)名でスレッドが作成されますThread[pool-1-thread-1]。ご覧のとおり、これはそれほど有用ではなく、私が知る限り、Executorフレームワークは作成されたスレッドまたはスレッドプールに名前を付ける簡単な方法を提供していません。

では、どのようにしてスレッド/スレッドプールの名前を提供するのでしょうか?例えば、Thread[FooPool-FooThread]

回答:


118

あなたは供給できるThreadFactoryまでnewSingleThreadScheduledExecutor(ThreadFactory threadFactory)。ファクトリはスレッドの作成を担当し、スレッドに名前を付けることができます。

Javadocを引用するには:

新しいスレッドを作成する

新しいスレッドはを使用して作成されThreadFactoryます。特に指定がない場合は、Executors.defaultThreadFactory()使用されている、それは同じで、すべてすることがスレッドを作成ThreadGroupし、それをNORM_PRIORITY優先順位と非デーモン状態。別のThreadFactoryを指定することにより、スレッドの名前、スレッドグループ、優先度、デーモンのステータスなどThreadFactoryを変更できます。からnullが返されてスレッドの作成に失敗した場合newThread、executorは続行しますが、タスクを実行できない可能性があります


283

グアバはあなたが必要とするものをほとんど常に持っています。

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

そしてそれをあなたに渡しますExecutorService


3
それは素晴らしいです!
Martin Vseticka

25
それは悲しいことだ!:-(
exic 2018

「guava」がどこにあるかわかりません。グーグルのグアバには多くの部分があり、同じ名前のライブラリが数十あります。search.maven.org/artifact/com.google.guava/guava/29.0-jre/…を意味していると思います。そうですか?あなたが提供するリンクはそれがグーグルからのものであることを示唆していますが、グーグルはMaven / Sonatypeに「guava」という名前のアーティファクトを約6個持っています。
Jason

@Jason-自明ではないJavaプロジェクトを作成している場合は、依存関係として既にguavaがあるはずです。そしてここにあります:github.com/google/guava
pathikrit

@pathikrit、ありがとう!グアバについてもっと勉強する必要があると思います:-)
ジェイソン

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
これに関する小さな問題の1つは、ドキュメントで説明されている障害動作が発生した場合です(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 2014

単に素晴らしい!ありがとうございました。
asgs

1
他の答えが言うように、これは名前を設定するための迅速で汚れた方法であり、複数のスレッドで設定すると、すべて同じ名前になります!!
Tano

終了時にスレッド名を元に戻す必要がある場合があります。これは、スレッドが関連しないさまざまなタスクで作業している場合でも、スレッド名が保持される可能性があるためです。
ダスティンK

51

BasicThreadFactoryapacheのコモンズ-LANGからも命名の振る舞いを提供するのに有用です。匿名の内部クラスを作成する代わりに、ビルダーを使用して必要に応じてスレッドに名前を付けることができます。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-プレフィックスが付けられます。ファクトリBeanで"myPool-"設定することにより、プレフィックスを明示的に別の値(たとえば)に設定できexecutorFactoryBean.setThreadNamePrefix("myPool-")ます。


CustomizableThreadFactoryが見つかりませんか?私はjdk 1.7を使用しています。ここで何が欠けているのですか?
カムランシャヒド

@KamranShahidこれはSpring Frameworkクラスです。これを使用するには、Springを使用する必要があります
Adam Michalik

20

Oracleには、このためのオープンRFEがあります。オラクルの従業員からのコメントから、彼らは問題を理解しておらず、修正できないようです。これは、JDKでサポートするのが(下位互換性を損なうことなく)非常に単純であるため、RFEが誤解されるのは一種の残念なことです。

指摘したように、独自のThreadFactoryを実装する必要があります。この目的のためだけにGuavaまたはApache CommonsをThreadFactory導入したくない場合は、ここで使用できる実装を提供します。これは、スレッド名の接頭辞を「プール」以外に設定できることを除いて、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();

スレッドが指定pool-N-thread-MされているExecutorServiceを提供しますが、

    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)

新しいスレッドを構築します。実装は、優先度、名前、デーモンのステータス、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を使用する必要があります。
Pino

提案をありがとう。私はあなたの提案を取り入れました。
Ravindra babu 2016年

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私はそれに応じて私の答えを編集しました。
ビクトル・ギル

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はGoogle Guavaライブラリからのものであることに注意してください。
Craig Otis

3

シングルスレッドエグゼキューターの名前を変更するだけの場合は、ラムダをスレッドファクトリとして使用するのが最も簡単です。

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

これにより2つのスレッドが作成されます。1つは「あなたの名前」、もう1つは「pool-N-thread-M」
Systemsplanet

@Systemsplanetいいえ、ありません。番組次のスレッドをスリープスレッドを実行するエグゼキュータを使用し、最小限の例からスレッドダンプを取る:main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
CamW

うーん、試してみました。新しいRunnable()を渡すとスレッドが作成され、自分でスレッドを作成するので、それは当然のことです。
Systemsplanet

代わりにThreadPoolExecutorを使用したか、他の目的で実行されていると思います。このコードは、「pool-N-thread-M」スレッドを作成しません。また、それが意味があるとは思わない。「新しいRunnable()を渡すと、スレッドが作成される」という記述は正しくありません。これは、その実行可能ファイルを使用してスレッドを作成し、シングルスレッドエグゼキューターであるため、1回実行します。スレッドは1つだけ作成されます。
CamW

2

これは、スレッドダンプアナライザーのカスタマイズされた名前を提供する私のカスタマイズされたファクトリーです。通常は、tf=nullJVMのデフォルトのスレッドファクトリを再利用します。このWebサイトには、より高度なスレッドファクトリがあります。

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.