线程Id
Id从1开始,JVM运行起来之后,我们自己创建的线程ID早已不是2
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:11
从输出可以看到主线的Id为1,而我们自己创建的子线程Id为11,这就很奇怪了为什么线程Id不是从0开始,而且为什么我们自己创建的子线程Id就是11了呢。
可以从java的底层代码中看到,这个线程的Id初始值是0,再每一次生成Id的时候都是先自增的,所以起始值是1。
/* For generating thread ID */
private static long threadSeqNumber;
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
那么为什么我们自己创建的子线程Id是11呢,这个可以从Debug中查看。
在Debug下可以看到,在我们的子线程创建之前就已经有了其他线程创建了。
Signal Dispatcher是把操作系统的信号发给我们适当的程序的。
Reference Handler是和GC相关的引用线程。
Finalizer是负责执行Finalizer对象的方法。
这里其实是要说明,在我们创建自己的主/子线程的时候,JVM已经为我们创建了许多线程。
线程名字
默认名字
可以从java的源码看出,线程的默认名字是一个写死的“Thread-”加一个数字,这个数字是自增的,且加了synchronized 不会出现重复的情况。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
手动设置名字
从java的源码中可以看到,首先是一个健康检查,然后底下的setNativeName()是native方法(C/C++层面给线程的名字做一个设置),也就是当线程启动之后,这个名字是不能修改了;但是Java内部的线程名字是可以通过setName()的方式来进行修改名字的。为什么当线程起来之后不能修改,是因为他做了一个!=0(线程的状态)的判断,为0时就是线程还没有启动的时候。
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
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);
}
}
private native void setNativeName(String name);
守护线程
作用: 给用户线程提供服务。
他守护的就是我们用户自己编写的子线程。
用户线程和守护线程分类的标准就是取决于守护线程的特性,就是这个线程是否会组织JVM的停止。
当还有用户线程在执行的时候JVM是不会停止的,如果当前只剩下了守护线程,那么守护线程会随着JVM一起停止。
特性:
- 线程类型默认继承自父线程
我们自己创建的线程就是父线程,这是因为我们的父线程就是用户线程;同理守护线程创建的线程也是继承自己的父线程,也就是守护线程创建的线程还是守护线程。
如果我们要将用户线程设置为守护线程,是需要人为的为用户线程进行设置 - 被谁启动
通常而言,所有的守护线程都是由JVM启动。在JVM启动的时候有一个非守护线程,那就是main函数。 - 不影响 JVM退出
对于JVM退出的时候,他只看有没有用户线程。
守护线程和普通线程区别
- 整体上没有区别(只是代码的任务不一样)
- 唯一区别在于JVM的离开(如果是用户线程会影响JVM的退出,守护线程则不会)
我们不需要给线程设设置为守护现场,因为当我们自己的线程在访问某个文件或是执行某个操作的时候,JVM发现就只剩下守护线程了,那JVM会关闭,这时候就会导致数据不一致的情况。
线程优先级
10个级别,默认5
在Java底层有以下三个,最低,默认,最高的等级。
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
程序设计不应该依赖于优先级
- 不同的操作系统不一样(window是7个,linux优先级有一致)
- 优先级会被操作系统改变