文档章节

Java并发编程初级篇(九):线程组

阿拉德大陆的魔法师
 阿拉德大陆的魔法师
发布于 2016/11/23 17:33
字数 1110
阅读 71
收藏 0

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

 

© 著作权归作者所有

共有 人打赏支持
阿拉德大陆的魔法师
粉丝 22
博文 91
码字总数 83019
作品 0
西城
程序员
Java面试:投行的15个多线程和并发面试题

本文由ImportNew -一杯哈希不加盐 翻译自dzone。欢迎加入翻译小组。转载请见文末要求。 多线程和并发问题已成为各种 Java 面试中必不可少的一部分。如果你准备参加投行的 Java 开发岗位面试,...

ImportNew
08/23
0
0
Java Concurrent--java.util.Concurrent包

java.util.concurrent并发编程包是专门为Java并发编程设计的,其中设计的类主要分为以下几部分: 显式锁 原子变量 线程池 并发容器 同步工具类 1、显式锁 显式锁相关内容可以看这篇博客:htt...

akane_oimo
05/27
0
0
读书笔记之《Java并发编程的艺术》-并发编程基础

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
8
读书笔记之《Java并发编程的艺术》-并发编程容器和框架(重要)

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
1
读书笔记之《Java并发编程的艺术》-线程池和Executor的子孙们

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏
2015/11/11
0
1

没有更多内容

加载失败,请刷新页面

加载更多

为什么Java大神,都在看Spring Boot和Spring Cloud的书?

如果你是一名Java开发人员,并且最近正打算学习Spring Boot和Spring Cloud框架并寻找一些关于它们的最好的书籍,那么,你今天就来对地方了。 本文,我们将讨论一些学习Spring Boot和Spring ...

Java小铺
14分钟前
2
0
springboot logback日志配置

springboot 如果不使用外部tomcat的话,日志是需要自己配置的,不然的话就只有控制台的日志,但是日志又是我们在项目上了生产环境,出问题时,检查问题的唯一途径,所以我们要配置详细的日志...

曾大大胖
15分钟前
2
0
Linux服务器集体篡改时间的方法

Red Hat 虚拟化课程RH318,中小型公司使用的话,感觉可以匹敌OpenStack。手头上有一个VMware的映像,RHEV 3.5版的,只能把系统时间调整到2016年才能使用。Red Hat的RHEV已经更新到4.1版,不过...

大别阿郎
15分钟前
1
0
Tomcat启动异常:java.lang.ClassNotFoundException

警告: Name = mysqlDataSource Property maxActive is not used in DBCP2, use maxTotal instead. maxTotal default value is 8. You have set value of "100" for "maxActive" property, wh......

hengbao5
16分钟前
1
0
GO错误的一些处理(defer,recover,panic)

package main import("fmt""errors")func main() {num := 10fmt.Printf("num的类型%T, num的值%v, num的地址%v\n", num, num, &num)num2 := new(int) //返回一个指针//num...

汤汤圆圆
25分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部