java中默认启动就有2个线程组:
- system线程组
- main线程组
线程组之间是嵌套的关系,system线程组就嵌套了main线程组.
我们如果没有给线程指定线程组,则默认是创建在main线程组中的.同理线程池如果没有指定线程组,也是归属在main线程组中的.cuiyaonan2000@163.com
线程运行中途不能改变它所属的线程组,也就是说线程一旦指定所在的线程组,就直到该线程结束。
执行如下代码
package nan.yao.cui.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author: cuiyaonan2000@163.com
* @Description: todo
* @Date: Created at 2022-6-6 9:38
*/
public class Test01 implements Runnable{
public static void main(String[] args){
//连接池的代码
ExecutorService newFixThreadPool_1 = Executors.newCachedThreadPool();
newFixThreadPool_1.submit(new Test01());
// System.out.println("自定义的线程组");
// ThreadGroup tg = new ThreadGroup();
//
// new Thread();
// System.out.println();
//
// System.out.println(tg.getParent().getName());
}
public void showThreadGroup(Thread thread){
ThreadGroup threadGroup = thread.getThreadGroup();
do{
System.out.println("线程组的名字是:" + threadGroup.getName() +" 当前的优先级是:" + threadGroup.getMaxPriority());
System.out.println("是否是守护线程组:" + threadGroup.isDaemon() +" 当前线程组的线程数是:" + threadGroup.activeCount());
threadGroup = threadGroup.getParent();
}while(threadGroup != null );
}
@Override
public void run() {
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
do{
System.out.println("线程组的名字是:" + threadGroup.getName() +" 当前的优先级是:" + threadGroup.getMaxPriority());
System.out.println("是否是守护线程组:" + threadGroup.isDaemon() +" 当前线程组的线程数是:" + threadGroup.activeCount());
threadGroup = threadGroup.getParent();
}while(threadGroup != null );
}
}
ThreadGroup & ThreadPool
我们创建的线程必然是要有线程组的,(注意是一定会有线程组cuiyaonan2000@163.com).
线程组顾名思义就是用于批量管理线程,省去我们为每一个线程设置优先级,异常处理等信息.
线程池是用于管理线程的生命周期的,从创建,就绪,运行,等待,死亡,超时.另线程池里的线程创建默认也是制定了线程组的,即只要是线程一定会有个线程组.
Thread Must need ThreadGroup为什么我们创建的线程一定会设置线程组的原因?
Thread针对我们创建的线程,以new Thread 为例
如上Thread的构造方法中,如果要设置ThreadGroup则一定不能为空.
这里我们看下没有传递ThreadGroup的构造函数.以public Thread() 为例子
可以看到内部又调用了init()且传递的ThreadGroup为null
init() 内容如下
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
* @param inheritThreadLocals if {@code true}, inherit initial values for
* inheritable thread-locals from the constructing thread
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* Do we have the required permissions?
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
如下如果我们没有指定new Thread()的线程组,则默认会使用创建该子线程的线程组,如下图所示(一般为main()线程组)
如上我们可以在创建Thread中设置线程组.那自定义线程组的构造函数如下所示
这里主要看下只需要name的构造函数
由此可见我们创建的线程组一定是 main线程组的子线程组.
线程池的线程以代码如下代码为例:
//连接池的代码
ExecutorService newFixThreadPool_1 = Executors.newCachedThreadPool();
newFixThreadPool_1.submit(new Test01());
线程池的创建主要是如下的代码产生
在如下所示,线程池创建的线程所属的线程组是创建线程的线程组
ThreadGroup作用范围
这个很容易理解,就是为线程组的线程设置默认的属性和处理方法.(因为是默认的属性和方法,自定线程当然可以进行覆盖)
这里距离几种重要的属性和默认方法的设置
线程优先级优先级由1到10之间的整数表示。
优先级为1的线程优先级最低。优先级为10的线程具有最高优先级。
在Thread类中定义了三个常量来表示下表中列出的三个不同的线程优先级。
线程优先级常量整数值MIN_PRIORITY1NORM_PRIORITY5MAX_PRIORITY10 异常处理线程的异常处理有4个level
- 针对线程对象的方法setUncaughtExceptionHandler
- 针对线程类的静态方法setDefaultUncaughtExceptionHandler
- 自定义线程组类对象,重写uncaughtException
优先级必然是:线程对象设置>线程组对象设置>线程类静态方法设置
package nan.yao.cui.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author: cuiyaonan2000@163.com
* @Description: todo
* @Date: Created at 2022-6-6 9:38
*/
public class Test01 implements Runnable {
public static void main(String[] args) throws InterruptedException {
//针对线程类的静态方法
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
System.out.println("this is setDefaultUncaughtExceptionHandler .the thread" + t.getName() + " catch the exception:" + e.getMessage());
});
//相当于是集成了ThreadGroup并且重写了uncaughtException 方法
ThreadGroup tg = new ThreadGroup("myTG"){
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println( "线程组:" + getName() + "下的线程:"+ t.getName() + "出现了异常:" + e.getMessage());
}
};
//设置线程组的权限
tg.setMaxPriority(Thread.MAX_PRIORITY);
Test01 test01 = new Test01();
Test01 test02 = new Test01();
Test01 test03 = new Test01();
Test01 test04 = new Test01();
Thread t1 = new Thread(tg,test01,"第一个异常");
Thread t2 = new Thread(test01,"第二个异常");
//针对该线程的异常捕获
t2.setUncaughtExceptionHandler((t, e) -> {
System.out.println("this is setUncaughtExceptionHandler .the thread" + t.getName() +" catch the exception:" + e.getMessage());
});
//会使用默认的异常捕获
Thread t3 = new Thread(test03,"第三个异常");
Thread t4 = new Thread(tg,test04,"第四个异常");
t4.setUncaughtExceptionHandler((t, e) -> {
System.out.println("this is setUncaughtExceptionHandler .the thread" + t.getName() +" catch the exception:" + e.getMessage());
});
t1.start();
t2.start();
t3.start();
t4.start();
Thread.sleep(5000);
}
@Override
public void run() {
throw new RuntimeException("this is business exception ,throwed by " + Thread.currentThread().getName());
}
}