线程的四个属性
如上图所示,线程有四个属性:
- 线程ID
- 线程名称
- 守护线程
- 线程优先级
1. 线程ID
-
每个线程都有id,这个id不能修改
-
线程id会不停的自增,从1开始
-
main函数就是第一个线程,id=1
id 是操作系统用来识别各个线程的编号,具有唯一性,从下面 java 的源码中看到,这个线程的Id初始值是0,但是每一次生成Id的时候都是先自增的(++threadSeqNumber),所以起始值实际是1。
测试代码:
/******
* 线程Id从1开始,JVM运行起来后,自己创建的线程的Id早已不是0
*/
public class Id {
public static void main(String[] args) {
Thread thread = new Thread();
System.out.println("主线程Id:"+Thread.currentThread().getId());
System.out.println("子线程Id:"+thread.getId());
}
}
打印结果:
以上看到主线程的id为1,可以理解,那么为什么我们创建的子线程id就直接为12了???
我们打断点调试发现,在我们的子线程创建之前,JVM其实已经为我们创建了许多线程
- Signal Dispatcher是把操作系统的信号发给我们适当的程序的。
- Reference Handler是和GC相关的引用线程。
- Finalizer是负责执行Finalizer对象的方法。
2. 线程名字
(1) 默认线程名的源码分析
- 有参构造器,指定线程名
- 无参构造器,默认为
Thread-
,后接 nextThreadNum ,这个数字为0开始,自增
(2) 手动设置线程名字 setName()
从上图中 java 的源码可以看到,this.name = name 是修改 java 层面的线程的名字,然后 setNativeName() 是native方法(C/C++层面给线程的名字做一个设置),但当线程启动之后,这个C/C++层面的名字不能修改了;不过 Java 层面的线程名字还是可以修改的。为什么当线程起来之后C/C++层面不能修改,是因为他做了一个!=0(线程的状态)的判断,为0时就是线程 new 出来后还没有 start。
3. 守护线程
线程类型有两种:守护线程、用户线程
守护线程的作用: 给用户线程提供服务。
用户线程和守护线程分类的标准:这个线程是否会阻止 JVM 的退出。当还有用户线程在执行的时候JVM是不会停止的,如果当前只剩下了守护线程,那么守护线程会随着JVM一起停止。
线程类型的特性:
- 线程类型默认继承自父线程
- 通常而言,所有的守护线程都是由 JVM 启动。在 JVM 启动的时候有一个非守护线程,那就是main函数。
- 守护线程不影响 JVM退出,当 JVM 退出的时候,他只看有没有用户线程。
守护线程和用户线程区别
- 整体上没有区别(只是代码的任务不一样)
- 唯一区别在于是否影响 JVM 的退出(如果是用户线程会影响JVM的退出,守护线程则不会)
4. 线程优先级
Q:线程的优先级是什么?
A:在操作系统中,线程可以划分优先级,线程优先级越高,获得 CPU 时间片的概率就越大,但线程优先级的高低与线程的执行顺序并没有必然联系,优先级低的线程也有可能比优先级高的线程先执行。
优先级有10个级别,默认5,在Java源码中有三个定义,最低,默认,最高的优先级定义,如下图。
线程虽然有优先级这个设定,但是实际开发中尽量不要设置优先级,即对程序的设计不应该依赖于优先级,主要原因如下:
-
不同的操作系统优先级不同
-
window
中只有7个优先级,java 程序运行在 windows 系统时,会进行优先级映射时,就可能会有多个优先级映射成一个优先级 -
linux
中所有线程优先级会被忽略,所有线程优先级一致
-
-
优先级可能会被操作系统修改,即优先级高的程序不一定被优先执行