Đặt tên chủ đề và nhóm luồng của ExecutorService


228

Giả sử tôi có một ứng dụng sử dụng Executorkhung như vậy

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

Khi tôi chạy ứng dụng này trong trình gỡ lỗi, một luồng được tạo với tên (mặc định) sau : Thread[pool-1-thread-1]. Như bạn có thể thấy, điều này không hữu ích lắm và theo như tôi có thể nói, Executorkhung công tác không cung cấp một cách dễ dàng để đặt tên cho các chủ đề hoặc nhóm chủ đề đã tạo.

Vì vậy, làm thế nào để đi cung cấp tên cho chủ đề / nhóm chủ đề? Ví dụ , Thread[FooPool-FooThread].

Câu trả lời:


118

Bạn có thể cung cấp một ThreadFactorytới newSingleThreadScheduledExecutor(ThreadFactory threadFactory). Nhà máy sẽ được responsibe để tạo chủ đề và có thể đặt tên cho chúng.

Để trích dẫn Javadoc :

Tạo chủ đề mới

Chủ đề mới được tạo bằng cách sử dụng a ThreadFactory. Nếu không được chỉ định khác, a Executors.defaultThreadFactory()được sử dụng, nó tạo ra các luồng cho tất cả giống nhau ThreadGroupvà có cùng NORM_PRIORITYtrạng thái ưu tiên và không phải daemon. Bằng cách cung cấp khác ThreadFactory, bạn có thể thay đổi tên luồng, nhóm luồng, mức độ ưu tiên, trạng thái trình nền, v.v ... Nếu ThreadFactorykhông tạo được luồng khi được yêu cầu bằng cách trả về null từ newThread, người thực thi sẽ tiếp tục, nhưng có thể không thực hiện được bất kỳ tác vụ nào


283

Quả ổi hầu như luôn có thứ bạn cần .

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

và chuyển nó cho bạn ExecutorService.


3
Điều đó thật tuyệt!
Martin Vseticka

25
Điều đó thật đáng buồn! :-(
exic

Tôi không chắc chắn nơi để tìm "ổi". Có rất nhiều phần của Guava của Google và có hàng tá thư viện có cùng tên. Tôi giả sử bạn có nghĩa là search.maven.org/artifact/com.google.guava/guava/29.0-jre/ mẹo . Có đúng không? Liên kết bạn cung cấp cho thấy nó là từ Google, nhưng Google cũng có khoảng nửa tá cổ vật trên Maven / Sonatype có tên là "ổi".
Jason

@Jason - Nếu bạn đang viết một dự án Java không tầm thường, rất có thể bạn đã có ổi như một phụ thuộc. Và đây là: github.com/google/guava
pathikrit

@pathikrit, cảm ơn! Tôi nghĩ rằng tôi cần nghiên cứu về Guava nhiều hơn :-)
Jason

95

Bạn có thể cố gắng cung cấp nhà máy luồng của riêng bạn, nơi sẽ tạo ra luồng với tên thích hợp. Đây là một ví dụ:

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

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

58

Bạn cũng có thể thay đổi tên của chủ đề của mình sau đó, trong khi chủ đề được thực thi:

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

Điều đó có thể được quan tâm nếu ví dụ bạn đang sử dụng cùng một ThreadFactory cho các loại nhiệm vụ khác nhau.


7
Điều này hoạt động tốt bởi vì như mô tả của FlorianT, tôi có nhiều loại luồng khác nhau và không muốn phải tạo nhiều đối tượng ThreadFactory chỉ cho tên. Tôi đã gọi Thread.cienThread (). SetName ("FooName"); là dòng đầu tiên trong mỗi phương thức run ().
Robin Zimmermann

5
Một vấn đề nhỏ với điều này là khi hành vi thất bại được mô tả trong các tài liệu xảy ra : (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.). Nếu ExecutorService thay thế luồng, nó sẽ được ThreadFactory đặt tên. Sau đó, một lần nữa, thấy tên biến mất trong khi gỡ lỗi có thể là một chỉ báo hữu ích.
sethro

Đơn giản là tuyệt vời! Cảm ơn bạn.
vào

1
Như câu trả lời khác nói, đây là một phương pháp nhanh và bẩn để đặt tên và nếu bạn làm như vậy với nhiều luồng, tất cả sẽ có cùng tên !!
Tano

Có thể muốn đặt tên luồng trở lại ban đầu khi thoát, bởi vì nó có thể giữ lại tên ngay cả khi nó đang làm việc trên các tác vụ không liên quan khác nhau.
Dustin K

51

Các BasicThreadFactorytừ apache commons-lang cũng rất hữu ích để cung cấp các hành vi đặt tên. Thay vì viết một lớp bên trong ẩn danh, bạn có thể sử dụng Builder để đặt tên cho các chủ đề theo ý muốn. Đây là ví dụ từ 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

Nếu bạn đang sử dụng Spring, CustomizableThreadFactorybạn có thể đặt tiền tố tên luồng.

Thí dụ:

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

Ngoài ra, bạn có thể tạo ExecutorServicemột bean Spring bằng cách sử dụng ThreadPoolExecutorFactoryBean- sau đó tất cả các chủ đề sẽ được đặt tên bằng beanName-tiền tố.

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

Trong ví dụ trên, các chủ đề sẽ được đặt tên bằng myExecutor-tiền tố. Bạn có thể đặt tiền tố rõ ràng thành một giá trị khác (ví dụ "myPool-":) bằng cách đặt executorFactoryBean.setThreadNamePrefix("myPool-")trên bean nhà máy.


không thể tìm thấy CustomizableThreadFactory? tôi đang sử dụng jdk 1.7. bất cứ ý tưởng những gì tôi đang thiếu ở đây?
Kamran Shahid

@KamranShahid đây là lớp Spring Framework, bạn phải sử dụng Spring để có nó
Adam Michalik

20

Có một RFE mở cho điều này với Oracle. Từ các ý kiến ​​từ nhân viên của Oracle, có vẻ như họ không hiểu vấn đề và sẽ không khắc phục. Đây là một trong những điều rất đơn giản để hỗ trợ trong JDK (không phá vỡ tính tương thích ngược), vì vậy thật đáng tiếc khi RFE bị hiểu lầm.

Như đã chỉ ra, bạn cần triển khai ThreadFactory của riêng mình . Nếu bạn không muốn sử dụng Guava hoặc Apache Commons chỉ cho mục đích này, tôi cung cấp ở đây một ThreadFactorytriển khai mà bạn có thể sử dụng. Nó hoàn toàn giống với những gì bạn nhận được từ JDK ngoại trừ khả năng đặt tiền tố tên luồng thành một thứ khác ngoài "pool".

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

Khi bạn muốn sử dụng nó, bạn chỉ cần tận dụng thực tế là tất cả các Executorsphương pháp cho phép bạn cung cấp cho riêng bạn ThreadFactory.

Điều này

    Executors.newSingleThreadExecutor();

sẽ cung cấp một ExecutorService nơi các chủ đề được đặt tên pool-N-thread-Mnhưng bằng cách sử dụng

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

bạn sẽ nhận được một ExecutorService nơi các chủ đề được đặt tên primecalc-N-thread-M. Voila!


Bạn đã bỏ lỡ dấu ngoặc đơn đóng trong đoạn trích cuối cùng của mình
k.liakos

Chỉ cần một lưu ý nhanh chóng mà SonarLint / Qube thích không sử dụng ThreadGroupủng hộ ThreadPoolExecutor.
Drakes

8
private class TaskThreadFactory implements ThreadFactory
{

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

        return t;
    }

}

Truyền ThreadFactory cho một dịch vụ thực thi và bạn tốt để đi


8

Một cách nhanh chóng và bẩn là sử dụng Thread.currentThread().setName(myName);trong run()phương pháp.


7

Mở rộng ThreadFactory

public interface ThreadFactory

Một đối tượng tạo chủ đề mới theo yêu cầu. Việc sử dụng các nhà máy xử lý sẽ loại bỏ các cuộc gọi đến Chủ đề mới, cho phép các ứng dụng sử dụng các lớp con luồng ưu tiên đặc biệt, các ưu tiên, v.v.

Thread newThread(Runnable r)

Xây dựng một chủ đề mới. Việc triển khai cũng có thể khởi tạo mức độ ưu tiên, tên, trạng thái daemon, Threadgroup, v.v.

Mã mẫu:

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

đầu ra:

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

....Vân vân


1
Bộ đếm luồng của bạn không an toàn cho luồng: bạn nên sử dụng một AtomicInteger.
Pino

Cảm ơn đề nghị. Tôi đã kết hợp đề nghị của bạn.
Ravindra babu

5

Như các câu trả lời khác đã nói, bạn có thể tạo và sử dụng java.util.concurrent.ThreadFactorygiao diện của riêng bạn (không yêu cầu thư viện bên ngoài). Tôi đang dán mã của mình bên dưới vì nó khác với các câu trả lời trước vì nó sử dụng String.formatphương thức và lấy tên cơ sở cho các luồng làm đối số của hàm tạo:

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

Và đây là một ví dụ về việc sử dụng:

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

EDIT : làm cho ThreadFactorychủ đề triển khai của tôi an toàn, cảm ơn @mchernyakov đã chỉ ra nó.
Mặc dù không có ThreadFactorytài liệu nào nói rằng việc triển khai của nó phải an toàn cho luồng, nhưng thực tế DefaultThreadFactorylà an toàn luồng là một gợi ý lớn:

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
Bộ đếm luồng của bạn (threadNum) không phải là chủ đề an toàn, bạn nên sử dụng AtomicInteger.
mchernyakov

Cảm ơn bạn đã chỉ ra, @mchernyakov Tôi vừa chỉnh sửa câu trả lời của mình cho phù hợp.
Víctor Gil

4

Giải pháp Java lõi được trồng tại nhà mà tôi sử dụng để trang trí các nhà máy hiện có:

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

Trong hành động:

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

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

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

3

Bạn có thể viết triển khai ThreadFactory của riêng bạn, ví dụ sử dụng một số triển khai hiện có (như defaultThreadFactory) và thay đổi tên ở cuối.

Ví dụ về triển khai 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;
    }
}

Và cách sử dụng:

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

3

Tôi sử dụng để làm tương tự như dưới đây (yêu cầu guavathư viện):

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

1
Đáng chú ý ThreadFactoryBuilderlà từ thư viện Google Guava.
Craig Otis

3

Tôi thấy dễ dàng nhất để sử dụng lambda như một nhà máy xử lý luồng nếu bạn chỉ muốn thay đổi tên cho một người thực hiện luồng duy nhất.

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

Điều này tạo ra hai chủ đề. Một cái tên là "Tên của bạn" và một "pool-N-thread-M" khác
Systemsplanet

@Systemsplanet Không, không. Lấy một luồng xử lý từ một ví dụ tối thiểu sử dụng trình thực thi để chạy một luồng mà ngủ sẽ hiển thị các luồng sau:main@1, Finalizer@667, Reference Handler@668, Your name@665, Signal Dispatcher@666
CamW

Hum, nó đã làm khi tôi thử nó. Điều này có ý nghĩa rằng nếu bạn truyền cho nó một Runnable () mới, nó sẽ tạo ra một luồng cho bạn và bạn đang tự tạo một luồng.
Systemsplanet

Tôi hy vọng rằng bạn đã sử dụng một ThreadPoolExecutor thay thế hoặc có một hoạt động cho một số mục đích khác. Mã này sẽ không tạo ra một luồng "pool-N-thread-M". Ngoài ra, tôi không tin rằng nó có ý nghĩa rằng nó sẽ. Câu lệnh của bạn "nếu bạn truyền cho nó một Runnable () mới, nó sẽ tạo ra một luồng cho bạn" là không chính xác. Nó sử dụng runnable đó để tạo ra một luồng và thực hiện điều đó một lần bởi vì đó là một trình thực thi một luồng. Chỉ có 1 chủ đề được tạo.
CamW

2

Đây là nhà máy tùy chỉnh của tôi cung cấp một tên tùy chỉnh cho máy phân tích kết xuất luồng. Thông thường tôi chỉ cung cấp tf=nullđể sử dụng lại nhà máy luồng mặc định JVM. Trang web này có nhà máy chủ đề tiên tiến hơn.

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

Để thuận tiện cho bạn, đây là một vòng lặp luồng cho mục đích gỡ lỗi.

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

Điều này làm việc rất tốt cho tôi, hơi ngạc nhiên vì nó không được nâng cấp nhiều. Dù là cách cổ vũ.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.