1. 程式人生 > >執行緒池中的ThreadGroup坑

執行緒池中的ThreadGroup坑

前言

在Java中每一個執行緒都歸屬於某個執行緒組管理的一員,例如在主函式main()主工作流程中產生一個執行緒,則產生的執行緒屬於main這個執行緒組管理的一員。簡單地說,執行緒組(ThreadGroup)就是由執行緒組成的管理執行緒的類,這個類是java.lang.ThreadGroup類。
定義一個執行緒組,通過以下程式碼可以實現。
ThreadGroup group=new ThreadGroup(“groupName”);
Thread thread=new Thread(group,”the first thread of group”);
ThreadGroup類中的某些方法,可以對執行緒組中的執行緒產生作用。例如,setMaxPriority()方法可以設定執行緒組中的所有執行緒擁有最大的優先權。
所有執行緒都隸屬於一個執行緒組。那可以是一個預設執行緒組(不指定group),亦可是一個建立執行緒時明確指定的組。在建立之初,執行緒被限制到一個組裡,而且不能改變到一個不同的組。每個應用都至少有一個執行緒從屬於系統執行緒組。若建立多個執行緒而不指定一個組,它們就會自動歸屬於系統執行緒組。
執行緒組也必須從屬於其他執行緒組。必須在構建器裡指定新執行緒組從屬於哪個執行緒組。若在建立一個執行緒組的時候沒有指定它的歸屬,則同樣會自動成為系統執行緒組的一名屬下。因此,一個應用程式中的所有執行緒組最終都會將系統執行緒組作為自己的“父”。
那麼假如我們需要線上程池中實現一個帶自定義ThreadGroup的執行緒分組,該怎麼實現呢?
我們在給執行緒池(ThreadPoolExecutor)提交任務的時候可以通過execute(Runnable command)來將一個執行緒任務加入到該執行緒池,那麼我們是否可以通過new一個指定了ThreadGroup的Thread例項來加入執行緒池來達到前面說到的目的呢?

ThreadGroup是否可行

通過new Thread(threadGroup,runnable)實現執行緒池中任務分組

public static void main(String[] args) {
        ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
        final ThreadGroup group = new ThreadGroup("Main_Test_Group");
        for (int i = 0; i < 5
; i++) { Thread thread = new Thread(group, new Runnable() { @Override public void run() { int sleep = (int)(Math.random() * 10); try { Thread.sleep(1000 * 3); System.out.println(Thread.currentThread().getName()+"執行完畢"
); System.out.println("當前執行緒組中的執行執行緒數"+group.activeCount()); } catch (InterruptedException e) { e.printStackTrace(); } } }, group.getName()+" #"+i+""); pool.execute(thread); } }

執行結果
pool-1-thread-3執行完畢
pool-1-thread-1執行完畢
當前執行緒組中的執行執行緒數0
pool-1-thread-2執行完畢
當前執行緒組中的執行執行緒數0
當前執行緒組中的執行執行緒數0
pool-1-thread-4執行完畢
pool-1-thread-5執行完畢
當前執行緒組中的執行執行緒數0
當前執行緒組中的執行執行緒數0

執行結果中可以看到group中的執行緒並沒有因為執行緒池啟動了這個執行緒任務而執行起來.因此通過執行緒組來對執行緒池中的線層任務分組不可行.
從java.util.concurrent.ThreadPoolExecutor原始碼中可以看到如下建構函式:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

如果我們在例項化ThreadPoolExecutor時不指定ThreadFactory,那麼將以預設的ThreadFactory來建立Thread.

Executors內部類DefaultThreadFactory

下面的原始碼即是預設的Thread工廠

static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

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

從唯一的建構函式可以看到DefaultThreadFactory以SecurityManager 例項中的ThreadGroup來指定執行緒的group,如果SecurityManager 獲取到的ThreadGroup為null才預設以當前執行緒的group來指定.public Thread newThread(Runnable r) 則以group來new 一個Thead.這樣我們可以在例項化ThreadPoolExecutor物件的時候在其建構函式內傳入自定義的ThreadFactory例項即可達到目的.

public class MyTheadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    private ThreadGroup defaultGroup;

    public MyTheadFactory() {
        SecurityManager s = System.getSecurityManager();
        defaultGroup = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
    }

    public MyTheadFactory(ThreadGroup group) {
       this.defaultGroup = group;
        namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
    }

    public Thread newThread(Runnable r) {

        Thread t = new Thread(defaultGroup, null, namePrefix + threadNumber.getAndIncrement(), 0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}