Java并发编程系列文章
《一》多线程基础——Java线程与进程的基本概念
《二》多线程基础——Java线程入门类和接口
《三》多线程基础——Java线程组和线程优先级
《四》多线程基础——Java线程生命周期及转换
《五》多线程基础——Java线程间的通信(互斥与协作)
《六》实际应用——如何优雅的关闭线程
《七》实际应用——生产者与消费者模型
并发编程(多线程)
一直以来都是程序员头疼的难题。曾经听别人总结过并发编程的第一原则,那就是不要写并发程序,哈哈哈。后来发现,这样能够显著提高程序响应和吞吐量的利器,哪还能忍得住不会用呢?
整理出《Java并发编程系列文章》,共计7篇,与君共勉。
《三》多线程基础——Java线程组和线程优先级
- 1、线程组概念
- 2、线程组的优先级
- 3、线程组的倒置树形结构
- 4、线程组的常用方法
- 4.1、activeCount()
- 4.2、activeGroupCount()
- 4.3、enumerate(Thread[] list)
- 4.4、enumerate(ThreadGroup[] list, boolean recurse)
- 4.5、getMaxPriority()
- 4.6、getName()
- 4.7、getParent()
- 4.8、interrupt()
- 4.9、isDaemon()
- 4.10、isDestroyed()
- 4.11、list()
- 4.12、parentOf(ThreadGroup g)
- 4.13、setDaemon(boolean daemon)
- 4.14、setMaxPriority(int pri)
- 5、线程组的统一异常处理
1、线程组概念
Java中用ThreadGroup
来表示线程组,线程组最主要的作用就是对线程进行批量控制。每个Thread
必然存在于⼀个ThreadGroup
中。执⾏main()
⽅法线程组的名字是main,如果在new Thread
时没有显式指定,那么默认将父线程(当前执⾏new Thread的线程)线程组设置为自己的线程组。
线程组可以起到统⼀控制线程的优先级和检查线程的权限的作⽤。
2、线程组的优先级
Java中线程优先级可以指定,范围是1~10。这只是给操作系统⼀个优先级的参考值,线程最终在操作系统的优先级是多少还是由操作系统决定。
Java默认的线程优先级为5,线程的优先级会在线程被调⽤之前设定。通常情况下,⾼优先级的线程将会⽐低优先级的线程有更⾼的⼏率得到执⾏。我们使⽤⽅法 Thread 类
的 setPriority()
实例⽅法来设定线程的优先级。
public class Demo {
public static void main(String[] args) {
Thread a = new Thread();
System.out.println("我是默认线程优先级:"+a.getPriority());
Thread b = new Thread();
b.setPriority(10);
System.out.println("我是设置过的线程优先级:"+b.getPriority());
}
}
输出:
我是默认线程优先级:5
我是设置过的线程优先级:10
注意,如果线程优先级⼤于线程所在线程组的最大优先级,那么该线程的优先级将会失效,取⽽代之的是线程组的最大优先级。
什么是守护线程?
如果某线程是守护线程,那如果所有的⾮守护线程结束,这个守护线程也会⾃动结束。
应⽤场景是:当所有⾮守护线程结束时,结束其余的⼦线程(守护线程)⾃动关闭,就免去了还要继续关闭⼦线程的麻烦。
⼀个线程默认是非守护线程,可以通过Thread类的setDaemon(boolean on)来设置。
3、线程组的倒置树形结构
我们常常用线程池管理线程,但有时候只需要new少数的几个线程,就能处理业务时,我们可以选择使用线程组管理线程。线程组里可以包含线程,也可以包含线程组。呈现一种倒置树形结构,如下所示:
代码实现如下:
ThreadGroup tg = new ThreadGroup("tg");
Thread t1 = new Thread(tg,"t1"){
@Override
public void run() {
}
};
Thread t2 = new Thread(tg,"t2"){
@Override
public void run() {
}
};
ThreadGroup tg1 = new ThreadGroup(tg,"tg1");
ThreadGroup tg2 = new ThreadGroup(tg,"tg2");
Thread t1_1 = new Thread(tg1, new Runnable() {
@Override
public void run() {
}
},"t1_1");
ThreadGroup tg1_1 = new ThreadGroup(tg1,"tg1_1");
Thread t2_1 = new Thread(tg2, new Runnable() {
@Override
public void run() {
}
},"t2_1");
特别注意:程序入口main方法也是一个线程,是JVM创建的线程。此线程的名称是main,所属的线程组是main
4、线程组的常用方法
4.1、activeCount()
activeCount() 返回这个线程组和它的子组中活动线程数的估计值。例如:将tg线程组的活跃线程打印出来,将main线程组及其子组下的活跃线程打印出来。
ThreadGroup tg = new ThreadGroup("tg");
Thread t1 = new Thread(tg,"t1"){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
/* tg线程组的活跃线程打印出来 */
Thread[] ts = new Thread[tg.activeCount()];
tg.enumerate(ts);
Arrays.asList(ts).forEach(t -> System.out.println(tg.getName() + "线程组内容打印:" + t));
/* main线程组及其子组下的活跃线程打印出来 */
Thread[] ts_p = new Thread[tg.getParent().activeCount()];
tg.getParent().enumerate(ts_p);
Arrays.asList(ts_p).forEach(t -> System.out.println(tg.getParent().getName() + "线程组内容打印:" + t));
tg线程组内容打印:Thread[t1,5,tg]
main线程组内容打印:Thread[main,5,main]
main线程组内容打印:Thread[Monitor Ctrl-Break,5,main]
main线程组内容打印:Thread[t1,5,tg]
4.2、activeGroupCount()
activeGroupCount() 返回这个线程组和它的子组中的活动组数的估计值。例如:将tg线程组的线程组打印出来,将main线程组及其子组下的线程组打印出来。
ThreadGroup tg = new ThreadGroup("tg");
Thread t1 = new Thread(tg,"t1"){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
/* tg线程组的线程组打印出来 */
ThreadGroup[] tgs = new ThreadGroup[tg.activeGroupCount()];
tg.enumerate(tgs);
Arrays.asList(tgs).forEach(t -> System.out.println(tg.getName() + "线程组内容打印:" + t));
/* main线程组及其子组下的线程组打印出来 */
ThreadGroup[] tgs_p = new ThreadGroup[tg.getParent().activeGroupCount()];
tg.getParent().enumerate(tgs_p);
Arrays.asList(tgs_p).forEach(t -> System.out.println(tg.getParent().getName() + "线程组内容打印:" + t));
main线程组内容打印:java.lang.ThreadGroup[name=tg,maxpri=10]
4.3、enumerate(Thread[] list)
enumerate(Thread[] list) 在这个线程组及其子组中的每一个活动线程中拷贝到指定的数组中。示例如上2.1。
4.4、enumerate(ThreadGroup[] list, boolean recurse)
enumerate(ThreadGroup[] list, boolean recurse) 在这个线程组中的每一个活动分组中的指定数组引用。示例如上2.2。
4.5、getMaxPriority()
getMaxPriority() 返回此线程组的最大优先级。
4.6、getName()
getName() 返回这个线程组的名称。
4.7、getParent()
getParent()返回这个线程组的父。
4.8、interrupt()
interrupt() 中断这个线程组中的所有线程。
4.9、isDaemon()
isDaemon() 判断该线程组是否是守护线程组。
4.10、isDestroyed()
isDestroyed() 测试这个线程组是否已被销毁。
4.11、list()
list() 将此线程组的信息打印到标准输出。
4.12、parentOf(ThreadGroup g)
parentOf(ThreadGroup g) 测试这个线程组是否是线程组参数或它的一个祖先线程组的一个。
4.13、setDaemon(boolean daemon)
setDaemon(boolean daemon) 改变该线程组的守护状态。
4.14、setMaxPriority(int pri)
setMaxPriority(int pri) 设置组的最大优先级。
5、线程组的统一异常处理
public class ThreadGroupDemo {
public static void main(String[] args) {
ThreadGroup threadGroup1 = new ThreadGroup("group1") {
// 继承ThreadGroup并重新定义以下⽅法
// 在线程成员抛出unchecked exception
// 会执⾏此⽅法
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName() + ": " + e.getMessage());
}
};
// 这个线程是threadGroup1的⼀员
Thread thread1 = new Thread(threadGroup1, new Runnable() {
public void run() {
// 抛出unchecked异常
throw new RuntimeException("测试异常");
}
});
thread1.start();
}
}