ExecutorService iş parçacıklarının ve iş parçacıklarının adlandırılması


228

Diyelim ki bu Executorçerçeveyi kullanan bir uygulamam var

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

Bu uygulamayı hata ayıklayıcıda çalıştırdığımda, aşağıdaki (varsayılan) adla bir iş parçacığı oluşturulur: Thread[pool-1-thread-1] . Gördüğünüz gibi, bu çok kullanışlı değil ve söyleyebildiğim kadarıyla, Executorçerçeve oluşturulan iş parçacıklarını veya iş parçacığı havuzlarını adlandırmak için kolay bir yol sağlamaz.

Peki, iş parçacıkları / iş parçacığı havuzları için adlar nasıl verilir? Örneğin Thread[FooPool-FooThread],.

Yanıtlar:


118

Bir kaynağı olabilir ThreadFactoryiçin newSingleThreadScheduledExecutor(ThreadFactory threadFactory). Fabrika iş parçacığı oluşturmaktan sorumlu olacak ve bunları adlandırabilecektir.

Javadoc'u alıntılamak için :

Yeni konu oluşturma

Yeni evreler bir ThreadFactory . Aksi belirtilmedikçe, Executors.defaultThreadFactory()tümü aynı ThreadGroupve aynı NORM_PRIORITYöncelikli ve arka plan programı olmayan durum için iş parçacıkları oluşturan a kullanılır . Farklı tedarik ederek ThreadFactory, bir varsa parçacığının adını vb iplik grubu, önceliği, cini durumunu değiştirebilir ThreadFactorygelen null adlı dönerek sorduğu konu başlığı oluşturmak için başarısız newThreadinfaz devam edecek, ancak herhangi bir görevleri yürütmek mümkün olmayabilir


283

Guava neredeyse her zaman ihtiyacınız olan her şeye sahiptir .

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

ve kendinize verin ExecutorService.


3
Bu harika!
Martin Vseticka

25
Bu üzücü! :-(
exic

"Guava" yı nerede bulacağımı bilmiyorum. Google'ın Guava'sında birçok parça var ve aynı ada sahip onlarca kütüphane var. Demek istediğim search.maven.org/artifact/com.google.guava/guava/29.0-jre/… . Bu doğru mu? Sağladığınız bağlantı Google'dan geldiğini gösteriyor, ancak Google'ın Maven / Sonatype üzerinde "guava" adında yaklaşık yarım düzine eser var.
Jason

@Jason - Önemsiz bir Java projesi yazıyorsanız, büyük olasılıkla zaten bir bağımlılık olarak guava'ya sahip olmalısınız. Ve işte burada: github.com/google/guava
pathikrit

@pathikrit, teşekkürler! Sanırım Guava üzerinde daha fazla çalışmaya ihtiyacım var :-)
Jason

95

Uygun adlara sahip iş parçacığı oluşturacak olan kendi iş parçacığı fabrikanızı sağlamaya çalışabilirsiniz. İşte bir örnek:

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

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

58

Ayrıca, iş parçacığı yürütülürken iş parçacığınızın adını da değiştirebilirsiniz:

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

Örneğin, farklı türdeki görevler için aynı ThreadFactory kullanıyorsanız bu ilgi çekici olabilir.


7
FlorianT'nin açıkladığı gibi, pek çok farklı iş parçacığı türüm olduğu ve sadece ad için birden fazla ThreadFactory nesnesi oluşturmak istemediğinden bu güzel çalıştı. Thread.currentThread (). SetName ("FooName"); her run () yöntemindeki ilk satır olarak.
Robin Zimmermann

5
Docs açıklanan arıza davranış oluştuğunda bu Tek küçük bir konudur: (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 iş parçacığını değiştirirse, ThreadFactory tarafından adlandırılır. Daha sonra, hata ayıklama sırasında adın kaybolduğunu görmek yararlı bir gösterge olabilir.
sethro

Tek kelimeyle mükemmel! Teşekkür ederim.
asgs

1
Diğer cevabın dediği gibi, bu adı ayarlamak için hızlı ve kirli bir yöntemdir ve eğer birden fazla iş parçacığıyla yaparsanız, hepsi aynı ada sahip olacaktır !!
Tano

Çıkıştan sonra iş parçacığı adını orijinal olarak yeniden ayarlamak isteyebilirsiniz, çünkü farklı ilgisiz görevlerde çalışsa bile adı koruyabilir.
Dustin K

51

BasicThreadFactoryApache commons-lang dan da adlandırma davranış sağlamak için yararlıdır. Anonim bir iç sınıf yazmak yerine, iş parçacıklarını istediğiniz gibi adlandırmak için Oluşturucu'yu kullanabilirsiniz. Javadocs'tan örnek:

 // 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 kullanıyorsanız, bunun CustomizableThreadFactoryiçin bir iş parçacığı adı öneki ayarlayabilirsiniz.

Misal:

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

Alternatif olarak, ExecutorServicekullanarak bir Bahar fasulyesi olarak oluşturabilirsiniz ThreadPoolExecutorFactoryBean- o zaman ipliklerin hepsi beanName-önekle adlandırılır .

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

Yukarıdaki örnekte, iş parçacıkları myExecutor-önekle adlandırılacaktır . Fabrika çekirdeğini ayarlayarak öneki açıkça farklı bir değere (ör. "myPool-") Ayarlayabilirsiniz executorFactoryBean.setThreadNamePrefix("myPool-").


CustomizableThreadFactory bulamıyor musunuz? JDK 1.7 kullanıyorum. burada neyi kaçırdığımı biliyor musun?
Kamran Shahid

@KamranShahid bu bir Spring Framework sınıfı, Bahar için kullanmalısınız
Adam Michalik

20

Bir var açık RFE Oracle ile bunun için. Oracle çalışanından gelen yorumlardan, sorunu anlamadıkları ve düzeltilmeyecekleri anlaşılıyor. JDK'da desteklenmesi son derece basit olan şeylerden biri (geriye dönük uyumluluğu bozmadan), bu nedenle RFE'nin yanlış anlaşılması utanç verici.

Belirtildiği gibi kendi ThreadFactory'nizi uygulamanız gerekir . Guava veya Apache Commons'ı sadece bu amaçla çekmek istemiyorsanız, burada kullanabileceğiniz bir ThreadFactoryuygulama sağlarım. İş parçacığı adı önekini "havuz" dan başka bir şeye ayarlama yeteneği dışında JDK'dan aldığınıza tam olarak benzer.

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

Kullanmak istediğinizde, tüm Executorsyöntemlerin kendinize ait olmasını sağlamanızdan faydalanabilirsiniz ThreadFactory.

Bu

    Executors.newSingleThreadExecutor();

iş parçacığı adlandırılmış pool-N-thread-Mancak kullanarak bir ExecutorService verecektir

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

iş parçacıklarının adlandırıldığı bir ExecutorService alırsınız primecalc-N-thread-M. İşte bu kadar!


Son snippet'inizde bir kapanış parantezini kaçırdınız
k.liakos

SonarLint / Qube'nin ThreadGrouplehine kullanmamayı tercih ettiği kısa bir not 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'yi bir executerservice'e iletin ve hazırsınız


8

Hızlı ve kirli yolu kullanmaktır Thread.currentThread().setName(myName);içinde run()yöntemle.


7

İş parçacığını genişlet

public interface ThreadFactory

İstek üzerine yeni iş parçacıkları oluşturan bir nesne. İş parçacığı fabrikalarını kullanmak, yeni İş Parçacığına yapılan çağrıların kablo bağlantısını ortadan kaldırarak uygulamaların özel iş parçacığı alt sınıflarını, önceliklerini vb. Kullanmasını sağlar.

Thread newThread(Runnable r)

Yeni bir iş parçacığı oluşturur. Uygulamalar ayrıca öncelik, ad, arka plan durumu, ThreadGroup vb. Başlatabilir.

Basit kod:

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

çıktı:

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

....vb


1
İş parçacığı sayacınız iş parçacığı için güvenli değil: bir AtomicInteger kullanmalısınız.
Pino

Öneri için teşekkürler. Önerinizi dahil ettim.
Ravindra babu

5

Daha önce de belirtildiği gibi, java.util.concurrent.ThreadFactoryarayüzün kendi uygulamasını oluşturabilir ve kullanabilirsiniz (harici kütüphanelere gerek yoktur). Ben String.formatyöntemi kullanır ve bir yapıcı argüman olarak iş parçacığı için bir temel adı alır çünkü önceki cevaplardan farklı olduğu için aşağıdaki kod yapıştırıyorum :

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

Ve bu bir kullanım örneğidir:

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

EDIT : benim ThreadFactoryuygulama iş parçacığı güvenli hale, işaret için teşekkürler mchernyakov sayesinde . Belgelerin
hiçbir yerinde ThreadFactoryuygulamalarının iş parçacığı açısından güvenli olması gerektiği söylenmese de DefaultThreadFactory, iş parçacığının güvenli olduğu gerçeği büyük bir ipucu:

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
Sen iplik sayacı (threadsNum) threadsafe değil, AtomicInteger kullanmalısınız.
mchernyakov

İşaret ettiğiniz için teşekkürler, mchernyakov Cevabımı buna göre yeni düzenledim.
Víctor Gil

4

Mevcut fabrikaları dekore etmek için kullandığım evde yetiştirilen çekirdek Java çözümü:

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

Eylemde:

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

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

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

3

Örneğin, varolan bazı uygulamaları (defaultThreadFactory gibi) kullanarak kendi ThreadFactory uygulamanızı yazabilir ve sonunda adı değiştirebilirsiniz.

ThreadFactory uygulama örneği:

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

Ve kullanım:

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

3

Aşağıdaki gibi yapmak için kullanın ( guavakütüphane gerektirir ):

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

1
Bunu belirtmek gerekir ki ThreadFactoryBuilderGoogle Guava kütüphanesinden.
Craig Otis

3

Tek bir iş parçacığı yürütücüsünün adını değiştirmek istiyorsanız, bir lambda'yı iplik fabrikası olarak kullanmanın en kolay yolunu buluyorum.

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

bu iki iş parçacığı oluşturur. Biri "Adınız" ve diğeri "pool-N-thread-M" adlı
Systemsplanet

@Systemsplanet Hayır, değil. main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
Uyuyan

Hum, denediğimde oldu. Yeni bir Runnable () iletirseniz sizin için bir iş parçacığı oluşturuyor ve kendiniz bir iş parçacığı oluşturuyorsunuz.
Systemsplanet

Bunun yerine ThreadPoolExecutor kullandığınız veya başka bir amaçla çalışan biri olmasını bekliyorum. Bu kod bir "pool-N-thread-M" iş parçacığı oluşturmaz. Ayrıca, bunun mantıklı olacağına inanmıyorum. "Yeni bir Runnable () iletirseniz sizin için bir iş parçacığı oluşturur" deyim doğru değil. Bir iş parçacığı oluşturmak için bu runnable kullanır ve tek iş parçacığı yürütücü olduğu için bunu bir kez yapar. Yalnızca 1 iş parçacığı oluşturulur.
CamW

2

Bu benim iplik fabrikası analizörleri için özelleştirilmiş isimler sunan özelleştirilmiş fabrikam. Genellikle tf=nullJVM varsayılan iş parçacığı fabrikasını yeniden kullanıyorum. Bu web sitesi daha gelişmiş iplik fabrikasına sahiptir.

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

Size kolaylık sağlamak için bu, hata ayıklama amaçlı bir iplik dökümü döngüsüdür.

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

Bu benim için gerçekten iyi çalıştı, biraz şaşırdım değildi şaşırttı. Her iki şekilde de alkışlar.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.