Memberi penamaan utas dan kumpulan utas dari ExecutorService


228

Katakanlah saya memiliki aplikasi yang menggunakan Executorkerangka kerja seperti itu

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

Ketika saya menjalankan aplikasi ini di debugger, thread dibuat dengan berikut (default) Nama: Thread[pool-1-thread-1]. Seperti yang Anda lihat, ini tidak terlalu berguna dan sejauh yang saya tahu, Executorframework tidak memberikan cara yang mudah untuk memberi nama thread yang dibuat atau pool-thread.

Jadi, bagaimana cara menyediakan nama untuk utas / utas? Misalnya Thread[FooPool-FooThread],.

Jawaban:


118

Anda bisa menyediakan ThreadFactoryuntuk newSingleThreadScheduledExecutor(ThreadFactory threadFactory). Pabrik akan bertanggung jawab untuk membuat utas, dan akan dapat menamainya.

Mengutip Javadoc :

Membuat utas baru

Utas baru dibuat menggunakan a ThreadFactory. Jika tidak ditentukan lain, a Executors.defaultThreadFactory()digunakan, yang membuat utas untuk semua berada dalam status yang sama ThreadGroupdan dengan NORM_PRIORITYprioritas dan non-daemon yang sama. Dengan memberikan yang berbeda ThreadFactory, Anda dapat mengubah nama utas, grup utas, prioritas, status daemon, dll. Jika ThreadFactorygagal membuat utas saat diminta dengan mengembalikan nol dari newThread, pelaksana akan melanjutkan, tetapi mungkin tidak dapat menjalankan tugas apa pun


283

Jambu hampir selalu memiliki apa yang Anda butuhkan .

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

dan berikan kepada Anda ExecutorService.


3
Itu luar biasa!
Martin Vseticka

25
Itu menyedihkan! :-(
exic

Saya tidak yakin di mana menemukan "jambu biji". Ada banyak bagian untuk Google Guava dan ada puluhan perpustakaan dengan nama yang sama. Saya berasumsi maksud Anda search.maven.org/artifact/com.google.guava/guava/29.0-jre/… . Apakah itu benar? Tautan yang Anda berikan menunjukkan bahwa itu dari Google, tetapi Google juga memiliki sekitar setengah lusin artefak di Maven / Sonatype bernama "jambu".
Jason

@Jason - Jika Anda menulis proyek Java yang tidak sepele, kemungkinan besar Anda sudah memiliki jambu sebagai ketergantungan. Dan ini dia: github.com/google/guava
pathikrit

@pathikrit, terima kasih! Saya pikir saya perlu belajar lebih lanjut tentang Guava :-)
Jason

95

Anda dapat mencoba menyediakan pabrik utas sendiri, yang akan membuat utas dengan nama yang sesuai. Ini salah satu contohnya:

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

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

58

Anda juga dapat mengubah nama utas setelahnya, saat utas dijalankan:

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

Itu bisa menarik jika misalnya Anda menggunakan ThreadFactory yang sama untuk berbagai jenis tugas.


7
Ini berfungsi dengan baik karena seperti yang dijelaskan FlorianT, saya memiliki banyak jenis utas berbeda dan tidak ingin harus membuat beberapa objek ThreadFactory hanya untuk namanya. Saya menelepon Thread.currentThread (). SetName ("FooName"); sebagai baris pertama dalam setiap metode run ().
Robin Zimmermann

5
Salah satu masalah kecil dengan hal ini adalah ketika perilaku kegagalan dijelaskan dalam dokumentasi terjadi: (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.). Jika ExecutorService mengganti utas, itu akan dinamai oleh ThreadFactory. Kemudian lagi, melihat nama menghilang saat debugging bisa menjadi indikator yang berguna.
sethro

Cukup luar biasa! Terima kasih.
asgs

1
Seperti jawaban yang lain katakan, ini adalah metode cepat dan kotor untuk mengatur nama, dan jika Anda melakukannya dengan banyak utas, semua akan memiliki nama yang sama !!
Tano

Mungkin ingin mengatur nama utas kembali ke aslinya saat keluar, karena mungkin mempertahankan nama meskipun bekerja pada tugas yang berbeda terkait.
Dustin K

51

The BasicThreadFactorydari apache commons-lang juga berguna untuk menyediakan perilaku penamaan. Alih-alih menulis kelas dalam anonim, Anda dapat menggunakan Builder untuk memberi nama utas yang Anda inginkan. Inilah contoh dari 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

Jika Anda menggunakan Spring, ada CustomizableThreadFactory yang Anda dapat mengatur awalan nama utas.

Contoh:

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

Atau, Anda bisa membuatnya ExecutorServicemenggunakan kacang Spring menggunakan ThreadPoolExecutorFactoryBean- maka semua utas akan dinamai dengan beanName-awalan.

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

Pada contoh di atas, utas akan diberi nama dengan myExecutor-awalan. Anda dapat mengatur awalan secara eksplisit ke nilai yang berbeda (mis. "myPool-") Dengan menyetel executorFactoryBean.setThreadNamePrefix("myPool-")pada kacang pabrik.


tidak dapat menemukan CustomizableThreadFactory? saya menggunakan jdk 1.7. ada yang tahu apa yang saya lewatkan di sini?
Kamran Shahid

@KamranShahid ini adalah kelas Spring Framework, Anda harus menggunakan Spring untuk memilikinya
Adam Michalik

20

Ada RFE terbuka untuk ini dengan Oracle. Dari komentar dari karyawan Oracle tampaknya mereka tidak mengerti masalah ini dan tidak akan memperbaiki. Ini adalah salah satu dari hal-hal ini yang sangat sederhana untuk didukung di JDK (tanpa melanggar kompatibilitas ke belakang) sehingga agak memalukan bahwa RFE disalahpahami.

Seperti yang ditunjukkan, Anda perlu mengimplementasikan ThreadFactory Anda sendiri . Jika Anda tidak ingin menarik Guava atau Apache Commons hanya untuk tujuan ini saya berikan di sini ThreadFactoryimplementasi yang dapat Anda gunakan. Ini persis sama dengan apa yang Anda dapatkan dari JDK kecuali untuk kemampuan untuk mengatur awalan nama utas ke sesuatu yang lain selain "kumpulan".

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

Ketika Anda ingin menggunakannya, Anda cukup memanfaatkan fakta bahwa semua Executorsmetode memungkinkan Anda untuk menyediakannya sendiri ThreadFactory.

Ini

    Executors.newSingleThreadExecutor();

akan memberikan ExecutorService di mana utas diberi nama pool-N-thread-Mtetapi dengan menggunakan

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

Anda akan mendapatkan ExecutorService di mana utas diberi nama primecalc-N-thread-M. Voila!


Anda telah melewatkan tanda kurung penutup dalam cuplikan terakhir Anda
k.liakos

Hanya catatan singkat yang SonarLint / Qube lebih memilih untuk tidak menggunakan ThreadGroupmendukung ThreadPoolExecutor.
Drakes

8
private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}

Lewati ThreadFactory ke layanan eksekutor dan Anda siap melakukannya


8

Cara cepat dan kotor adalah dengan menggunakan Thread.currentThread().setName(myName);di run()metode.


7

Perpanjang ThreadFactory

public interface ThreadFactory

Objek yang membuat utas baru sesuai permintaan. Menggunakan pabrik utas menghilangkan pengabelan panggilan ke Thread baru, memungkinkan aplikasi menggunakan subclass utas khusus, prioritas, dll.

Thread newThread(Runnable r)

Membangun sebuah Thread baru. Implementasi juga dapat menginisialisasi prioritas, nama, status daemon, ThreadGroup, dll.

Kode sampel:

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

keluaran:

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

.... dll


1
Penghitung utas Anda tidak aman utas: Anda harus menggunakan AtomicInteger.
Pino

Terima kasih atas sarannya. Saya telah memasukkan saran Anda.
Ravindra babu

5

Seperti yang sudah dijawab oleh jawaban lain, Anda dapat membuat dan menggunakan implementasi java.util.concurrent.ThreadFactoryantarmuka Anda sendiri (tidak perlu pustaka eksternal). Saya menempelkan kode saya di bawah karena berbeda dari jawaban sebelumnya karena menggunakan String.formatmetode dan mengambil nama dasar untuk utas sebagai argumen konstruktor:

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

Dan ini adalah contoh penggunaan:

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

EDIT : membuat ThreadFactoryimplementasi saya aman, terima kasih kepada @mchernyakov karena menunjukkannya.
Meskipun tidak ada dalam ThreadFactorydokumentasi yang mengatakan bahwa implementasinya harus aman-utas, fakta bahwa DefaultThreadFactoryaman-utas adalah petunjuk besar:

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
Anda penghitung utas (threadsNum) bukan threadsafe, Anda harus menggunakan AtomicInteger.
mchernyakov

Terima kasih telah menunjukkannya, @mchernyakov Saya baru saja mengedit jawaban saya.
Víctor Gil

4

Solusi Java inti yang dikembangkan sendiri yang saya gunakan untuk menghias pabrik yang ada:

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

Beraksi:

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

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

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

3

Anda dapat menulis implementasi ThreadFactory Anda sendiri, menggunakan misalnya beberapa implementasi yang ada (seperti defaultThreadFactory) dan mengubah nama di akhir.

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

Dan penggunaan:

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

3

Saya menggunakan untuk melakukan hal yang sama seperti di bawah ini (memerlukan guavaperpustakaan):

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

1
Perlu dicatat bahwa ThreadFactoryBuilderitu dari perpustakaan Google Guava.
Craig Otis

3

Saya merasa paling mudah untuk menggunakan lambda sebagai pabrik utas jika Anda hanya ingin mengubah nama untuk satu pelaksana utas.

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

ini menciptakan dua utas. Satu bernama "Nama Anda" dan satu lagi "kumpulan-N-utas-M"
Systemsplanet

@ Systemsplanet Tidak, tidak. Mengambil dump thread dari contoh minimal yang menggunakan pelaksana untuk menjalankan utas yang tidur menunjukkan utas berikut:main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
CamW

Hum, itu terjadi ketika saya mencobanya. Masuk akal bahwa itu akan terjadi karena jika Anda memberikannya Runnable baru () itu membuat utas untuk Anda, dan Anda membuat utas sendiri.
Systemsplanet

Saya berharap bahwa Anda menggunakan ThreadPoolExecutor sebagai gantinya atau punya satu berjalan untuk tujuan lain. Kode ini tidak akan membuat utas "kumpulan-N-utas-M". Juga, saya tidak percaya itu masuk akal jika itu terjadi. Pernyataan Anda "jika Anda memberikannya Runnable baru () itu membuat utas untuk Anda" tidak benar. Ini menggunakan runnable itu untuk membuat utas dan melakukannya sekali karena itu adalah pelaksana satu-utas. Hanya 1 utas yang dibuat.
CamW

2

Ini adalah pabrik saya yang disesuaikan yang menyediakan nama yang disesuaikan untuk analisis penampung ulir. Biasanya saya hanya memberikan tf=nulluntuk menggunakan kembali pabrik thread JVM default. Situs web ini memiliki pabrik utas yang lebih canggih.

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

Untuk kenyamanan Anda, ini adalah loop dump thread untuk tujuan debug.

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

Ini bekerja dengan sangat baik bagi saya, agak terkejut itu tidak terlalu banyak dipilih. Either way ceria.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.