Java多线程编程(7)--线程组与线程工厂

一.线程组

  实际上,线程组是一个设计失败的概念。它最初是出于安全的考虑被设计用来隔离不同的applet(已过时)的。然而,ThreadGroup并未实现这一预期目标,并且它所实现的许多方法是有缺陷的。但是ThreadGroup仍然遗留在其他和线程有关的类中,因此这里只是对它进行一个简单的介绍。在大多数情况下,我们完全可以忽略线程组这一概念以及它的存在。
  一个线程组代表了一组线程。此外,一个线程组也能包含其他的线程组。线程与线程组的关系类似于文件和文件夹的关系,即一个文件总是位于特定的文件夹中,而一个文件夹可以包含多个文件和文件夹。通过线程组可以很方便的对多个线程进行管理。下图展示了线程组与线程及其他线程组的关系:

  上面的线程和线程组组成了一棵树,除了根线程组外,其他每个线程组都有一个父线程组。
  Thread类有几个构造器允许我们在创建线程的时候指定其所属的线程组。如果在创建线程的时候没有指定线程组,那么这个线程就属于其父线程所属的线程组。因此,每一个线程都一个线程组与之关联。

1.成员变量

  Java中使用ThreadGroup类来表示线程组。先来看这个类有哪些成员变量:

  • parent:父线程组;
  • name:线程组名称;
  • maxPriority:最大优先级。该线程组中的每个线程的优先级都不能超过这个值。
  • destroyed:该线程组是否已经被销毁。
  • daemon:是否为守护线程组。线程组是否为守护线程组与其管理的线程是否为守护线程没有关系,但概念类似。即若某线程组为守护线程组,则当该线程组的最后一个线程被停止或最后一个线程组被销毁后,该线程组将会自动销毁。
  • nUnstartedThreads:线程组中未启动的线程个数。
  • nthreads:线程组中已启动的线程个数,不包含子线程组中的线程;
  • threads:用来存储线程组中线程的数组,不包含未启动的线程;
  • ngroups:线程组中子线程组的个数,不包含子线程组中的线程组;
  • groups:用来存储线程组中子线程组的数组。

2.构造方法

  接着继续来看ThreadGroup的构造方法:

public ThreadGroup(String name) {
    this(Thread.currentThread().getThreadGroup(), name);
}

public ThreadGroup(ThreadGroup parent, String name) {
    this(checkParentAccess(parent), parent, name);
}

private ThreadGroup(Void unused, ThreadGroup parent, String name) {
    this.name = name;
    this.maxPriority = parent.maxPriority;
    this.daemon = parent.daemon;
    this.parent = parent;
    parent.add(this);
}

  ThreadGroup有两个public的构造方法可以供我们使用,其中ThreadGroup(ThreadGroup parent, String name)可以指定父线程组,而ThreadGroup(String name)则默认以当前线程所在的线程组作为父线程组。
  实际上,ThreadGroup还有一个无参的构造方法:

/**
 * Creates an empty Thread group that is not in any Thread group.
 * This method is used to create the system Thread group.
 */
private ThreadGroup() {     // called from C code
    this.name = "system";
    this.maxPriority = Thread.MAX_PRIORITY;
    this.parent = null;
}

  这个构造方法用于创建系统线程组,它会在JVM启动的时候被底层C语言代码来调用。系统线程组是所有线程组的祖先,它没有父线程组。

3.成员方法

  下面介绍ThreadGroup的几个成员方法:

  • int activeCount()

  返回线程组和子线程组中活跃的线程数。这是一个估值,因为在遍历线程的过程中,有些线程可能已经停止。

  • int activeGroupCount()

  返回线程组和子线程组中活跃的线程组数。线程组只有两种状态,active和destroyed,因此未被销毁的线程组都是活跃的线程组。这也是一个估值,因为在遍历线程组的过程中,有些线程组可能已经被销毁。

  • void destroy()

  销毁当前线程组。若线程组已被销毁或包含活跃的线程时该方法将会抛出异常。

  • int enumerate​(Thread[] list)

  将该线程组及子线程组中活跃的线程拷贝至指定的数组。

  • int enumerate​(Thread[] list, boolean recurse)

  将该线程组及子线程组(可选,recurse为false时只拷贝该线程组中活跃的线程)中活跃的线程拷贝至指定的数组。

  • int enumerate​(ThreadGroup[] list)

  将该线程组及子线程组中活跃的线程组拷贝至指定的数组。

  • int enumerate​(ThreadGroup[] list, boolean recurse)

  将该线程组及子线程组(可选,recurse为false时只拷贝该线程组中活跃的线程组)中活跃的线程组拷贝至指定的数组。

  • void interrupt()

  中断线程组中所有的线程。

二.线程工厂

  ThreadFactory接口是Java中的线程工厂,它是工厂模式的一种体现。它定义了如下工厂方法:

public Thread newThread(Runnable r);

  通过自定义线程工厂,我们可以在创建线程时对线程进行一些设置,例如是否是守护线程,统一给线程命名,设置线程优先级,设置线程组等,这使得在创建多个线程时无需手动调用Thread类的构造方法。例如Executors的newFixedThreadPoll(int nThreads, ThreadFactory threadFactory)方法,该方法会返回一个包含固定数量线程的线程池(有关线程池的内容会在后面的文章中介绍),我们可以将线程工厂传递给这个方法,这样线程池在创建线程时就会通过我们的线程工厂的方法去创建。
  例如,下面的线程工厂为每一个线程都设置了统一格式的名字:

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class MyThreadFactory implements ThreadFactory {
    private static final String THREAD_NAME_PREFIX = "mythread-";
    private static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1);

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, THREAD_NAME_PREFIX + THREAD_NUMBER.getAndIncrement());
    }
}
posted @ 2020-12-11 11:21  maconn  阅读(310)  评论(0编辑  收藏  举报