Executor
フレームワークを利用するアプリケーションがあるとします
Executors.newSingleThreadExecutor().submit(new Runnable(){
@Override
public void run(){
// do stuff
}
}
このアプリケーションをデバッガーで実行すると、次の(デフォルトの)名前でスレッドが作成されます:Thread[pool-1-thread-1]
。ご覧のとおり、これはそれほど有用ではなく、私が知る限り、Executor
フレームワークは、作成されたスレッドまたはスレッドプールに名前を付ける簡単な方法を提供しません。
それでは、スレッド/スレッドプールの名前をどのように提供するのでしょうか?たとえば、Thread[FooPool-FooThread]
。
グアバはほとんど常にあなたのものを持っています 必要 。
ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()
それをExecutorService
に渡します。
適切な名前のスレッドを作成する独自のスレッドファクトリを提供することができます。次に例を示します。
class YourThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r, "Your name");
}
}
Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);
スレッドの実行中に、後でスレッドの名前を変更することもできます。
Thread.currentThread().setName("FooName");
たとえば、異なるタイプのタスクに同じThreadFactoryを使用している場合、興味深いことがあります。
Apache commons-langの 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);
Oracleには、このための open RFE があります。オラクルの従業員からのコメントから、彼らは問題を理解しておらず、修正もしないようです。 JDKで(後方互換性を損なうことなく)サポートするのは非常に簡単なことの1つであるため、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"));
スレッドの名前がprimecalc-N-thread-M
であるExecutorServiceを取得します。出来上がり!
Springを使用している場合は、スレッド名のプレフィックスを設定できる CustomizableThreadFactory
があります。
例:
ExecutorService alphaExecutor =
Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));
private class TaskThreadFactory implements ThreadFactory
{
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "TASK_EXECUTION_THREAD");
return t;
}
}
ThreadFactoryをexecutorserviceに渡すと、準備完了です
すばやく汚い方法は、Thread.currentThread().setName(myName);
メソッドでrun()
を使用することです。
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
....等
たとえば、既存の実装(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")
);
Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());
Runnable getJob() {
return () -> {
// your job
};
}
これは、スレッドダンプアナライザーのカスタマイズされた名前を提供する私のカスタマイズされた工場です。通常、JVMのデフォルトのスレッドファクトリを再利用するためにtf=null
を指定します。 この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()
);
}
}
私は以下と同じように使用します(guava
ライブラリが必要です):
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("SO-POOL-%d").build();
ExecutorService executorService = Executors.newFixedThreadPool(5,namedThreadFactory);
単一のスレッドエグゼキュータの名前を変更するだけの場合は、ラムダをスレッドファクトリとして使用するのが最も簡単です。
Executors.newSingleThreadExecutor(runnable -> new Thread(runnable, "Your name"));
他の回答で既に述べたように、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);
既存の工場を装飾するために使用する自家製のコア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"));