Java API提供了一个线程组类ThreadGroup,这个类提供了一些方法可以让我们方便地对加入这个线程组的多个线程进行操作。
想使用线程组首先需要实例化一个线程组对象,并把创建的线程加入到这个线程组中。
ThreadGroup group = new ThreadGroup("Searcher");
Thread thread = new Thread(group, Runnable r);
查看Thread的源代码,我们发现在初始化Thread线程对象后,只是把ThreadGroup对象赋值给Thread类的group属性,只有当调用start()方法启动线程的时候,才真正的把线程加入到了线程组中。
/* The group of this thread */
private ThreadGroup group;
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
...
this.group = g;
...
)
public synchronized void start() {
...
group.add(this);
...
)
下面我们通过一个例子来看一看ThreadGroup为我们提供了那些有用的方法。
先定义一个Runnable类,在这个类中我们让线程休眠一个随机时间,如果在这个休眠时间内线程被中断那么打印中断信息,然后结束线程。如果线程成功执行完毕那么打印线程结束信息。
public class MyRunnable implements Runnable {
@Override
public void run() {
Random random = new Random(new Date().getTime());
int value = (int)(random.nextDouble() * 100);
System.out.printf("%s: Started and sleep %ds.\n", Thread.currentThread().getName(), value);
try {
TimeUnit.SECONDS.sleep(value);
} catch (InterruptedException e) {
System.out.printf("%s: Interrupted.\n", Thread.currentThread().getName());
return;
}
System.out.printf("%s: End.\n", Thread.currentThread().getName());
}
}
定义主方法类,我们创建10个线程并加入到线程组中。
public class Main {
public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("ThreadGroup");
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(group, new MyRunnable());
thread.start();
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//获取线程组中线程数量,并打印每一个线程信息。
System.out.printf("%s: Number of threads is %s.\n", Thread.currentThread().getName(), group.activeCount());
group.list();
//使用一个线程数组接收线程组中的所有线程
Thread[] threads = new Thread[group.activeCount()];
group.enumerate(threads);
for (int i = 0; i < threads.length; i++) {
System.out.printf("%s - %s\n", threads[i].getName(), threads[i].getState());
}
//等待第一个线程结束,然后中断剩余所有线程
while (group.activeCount() > 9) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
group.interrupt();
}
}
查看控制台日志来对ThreadGroup类的相关方法做一个解释。
首先创建并启动线程,线程内部打印启动信息:
Thread-0: Started and sleep 37s.
Thread-1: Started and sleep 33s.
Thread-2: Started and sleep 28s.
Thread-3: Started and sleep 13s.
Thread-4: Started and sleep 9s.
Thread-5: Started and sleep 12s.
Thread-6: Started and sleep 7s.
Thread-7: Started and sleep 2s.
Thread-8: Started and sleep 34s.
Thread-9: Started and sleep 30s.
接下来调用了ThreadGroup.activeCount()获取了线程组内的线程数量,并调用ThreadGroup.list()打印线程组内线程信息。
main: Number of threads is 10.
java.lang.ThreadGroup[name=ThreadGroup,maxpri=10]
Thread[Thread-0,5,ThreadGroup]
Thread[Thread-1,5,ThreadGroup]
Thread[Thread-2,5,ThreadGroup]
Thread[Thread-3,5,ThreadGroup]
Thread[Thread-4,5,ThreadGroup]
Thread[Thread-5,5,ThreadGroup]
Thread[Thread-6,5,ThreadGroup]
Thread[Thread-7,5,ThreadGroup]
Thread[Thread-8,5,ThreadGroup]
Thread[Thread-9,5,ThreadGroup]
下面调用ThreadGroup.enumerate(threads)方法用一个线程数组来接收线程组内的线程,并打印线程状态
Thread-0 - TIMED_WAITING
Thread-1 - TIMED_WAITING
Thread-2 - TIMED_WAITING
Thread-3 - TIMED_WAITING
Thread-4 - TIMED_WAITING
Thread-5 - TIMED_WAITING
Thread-6 - TIMED_WAITING
Thread-7 - TIMED_WAITING
Thread-8 - TIMED_WAITING
Thread-9 - TIMED_WAITING
最后我们等待第一个完成的线程后,利用ThreadGroup.interrupt()中断剩余所有线程。
Thread-7: End.
Thread-0: Interrupted.
Thread-2: Interrupted.
Thread-1: Interrupted.
Thread-9: Interrupted.
Thread-8: Interrupted.
Thread-4: Interrupted.
Thread-5: Interrupted.
Thread-6: Interrupted.
Thread-3: Interrupted.
另外通过查看ThreadGroup类的源代码你会发现他实现了异常处理接口Thread.UncaughtExceptionHandler,并重写了方法uncaughtException(),当然你也可以定义自己的类MyThreadGroup extends ThreadGroup,并重写uncaughtException()来实现自己的线程运行时异常处理逻辑。ThreadGroup类中使用的是Thread.getDefaultUncaughtExceptionHandler()来处理异常,要想让这个逻辑起作用,你需要使用Thread.setDefaultUncaughtExceptionHandler()静态方法来为Thread设置静态默认异常处理器,如果没有定义,同样线程会在控制台打印异常堆栈信息。
线程组中线程出现运行时异常会首先调用自己的异常处理器,如果没有定义则会调用线程组的异常处理器,如果还没有定义就会调用默认的异常处理器,并且当线程组中的一个线程抛出异常之后,其余线程都会设置中断状态。
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
}