本章主要介绍了所有与Thread有关的构造函数,线程的父子关系(并非继承关系,而是一种包含关系),Thread和ThreadGroup之间的关系,Thread与虚拟机栈的关系(学习这部分内容需要读者有JVM的相关基础,尤其是对栈内存要有深入的理解),最后还介绍了守护线程的概念、特点和使用场景。
线程命名
构造函数:
Constructor and Description |
---|
Thread() Allocates a new |
Thread(Runnable target) Allocates a new |
Thread(Runnable target, String name) Allocates a new |
Thread(String name) Allocates a new |
Thread(ThreadGroup group, Runnable target) Allocates a new |
Thread(ThreadGroup group, Runnable target, String name) Allocates a new |
Thread(ThreadGroup group, Runnable target, String name, long stackSize) Allocates a new |
Thread(ThreadGroup group, String name) Allocates a new
扫描二维码关注公众号,回复:
4856505 查看本文章
|
线程在启动之后,名称不允许修改:
/**
* Changes the name of this thread to be equal to the argument
* <code>name</code>.
* <p>
* First the <code>checkAccess</code> method of this thread is called
* with no arguments. This may result in throwing a
* <code>SecurityException</code>.
*
* @param name the new name for this thread.
* @exception SecurityException if the current thread cannot modify this
* thread.
* @see #getName
* @see #checkAccess()
*/
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}
线程的父子关系
Thread类所有的构造函数都会调用init方法,都会调用init方法,如下代码
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, name)}.
*
* @param name
* the name of the new thread
*/
public Thread(String name) {
init(null, null, name, 0);
}
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, name)}.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this thread's run method is invoked.
*
* @param name
* the name of the new thread
*/
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
查看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();
..................
}
任何一个线程都有一个父线程
currentThread 代表的是创建它的那个线程,我们创建的线程都是main函数创建的,意味着我们所有的创建的线程的父线程都是main线程
结论:
一个线程的创建肯定是由另一个线程完成的
被创建线程的父线程就是创建他的线程
Thread和ThreadGroup
在线程初始化的时候(init方法)可以指定ThreadGroup
/**
* 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) {
...................................
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();
}
测试代码:
结论:
main线程所在的ThreadGroup成为main
构造一个线程的时候,如果没有显示的指明ThreadGroup,那么她和所在的父线程同处一个ThreadGroup,并且具有同样的优先级
Thread和Runnable
Thread: 负责线程本身相关的职责和控制
Runnable: 逻辑执行单元的部分
Thread和JVM虚拟机栈
在Thread的构造函数中有一个stackSize , 代码如下:
/*
* The requested stack size for this thread, or 0 if the creator did
* not specify a stack size. It is up to the VM to do whatever it
* likes with this number; some VMs will ignore it.
*/
private long stackSize;
Thread和stackSize
一般情况下,创建线程的时候,不会手动指定栈内存的地址空间字节数组,统一通过xss参数设置即可
stacksize 越大代表着 线程内方法调用递归的深度就越深
stacksize 越小代表着 创建线程数量就越多(依赖平台)
对平台的依赖测试:
JVM内存结构
Java堆(Heap),是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
方法区(Method Area),方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
程序计数器(Program Counter Register),程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
JVM栈(JVM Stacks),与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
本地方法栈(Native Method Stacks),本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
太多了,就只是放了几张图。
Thread与虚拟机栈
虚拟机栈内存是线程私有的,也就是说每一个线程都会占有指定的内存大小。
可以粗略的理解一个JAVA进程的内存大小为: 堆内存+线程数量*栈内存
线程数量=(最大地址空间(MaxProcessMemory)- JVM堆内存 - ReserverdOsMemory / ThreadStackSize(Xss))
ReserverdOsMemory: 系统保留内存,一般在136Mb左右
线程数量还与操作系统的一些内核配置有很大的关系,如Linux下
/proc/sys/kernel/threads-max
/proc/sys/kernel/pid-max
/proc/sys/vm/max_map_count
守护线程
守护线程是一类比较特殊的线程,一般用于处理一些后台的工作。 如JDK的垃圾回收线程。
当你希望关闭某些线程,或者退出JVN进程的时候,一些线程能够自动关闭。
代码:
public class DamomThread {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread( ()-> {
while (true){
try {
Thread.sleep(1000);
System.out.println("内部线程:正在执行。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} ) ;
//开启守护进程
// thread.setDaemon(true);
thread.start();
Thread.sleep(2_000L );
System.out.println("Main thread finished lifecycle !!!!!!!!!!");
}
}
该代码一共开启了两个线程,一个是main线程,另一个是里面执行的线程thread
未开始守护进程: thread.setDaemon(false);
外面的线程main线程,结束退出,里面的线程,依旧继续执行,如截图
开始守护进程: thread.setDaemon(true);
main线程退出,内部线程一起退出。
本文整理来源于:
《Java高并发编程详解:多线程与架构设计》 --汪文君