6、核心六:线程的各个属性
6.1 ID
线程ID不能修改。从1开始ID自增,JVM运行起来之后,我们自己创建的线程ID早已不是0
public class Id {
public static void main (String[] args) {
Thread thread = new Thread();
System.out.println(Thread.currentThread().getId());//1
System.out.println(thread.getId());//16
}
}
调试一下为什么是16:JVM为我们自动创建了很多线程
源码
/*
* Thread ID
*/
private final long tid;
/* For generating thread ID */
private static long threadSeqNumber;
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
public long getId() {
return tid;
}
private Thread(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 manager doesn't have a strong opinion
on 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(
SecurityConstants.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 */
this.tid = nextThreadID();//重点
}
6.2 名字
名字一旦确定,以后可以改变。但是一旦线程启动起来,我们没有办法修改
源码
private volatile String name;
/* For autonumbering anonymous threads. */
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
public final String getName() {
return name;
}
public Thread() {
this(null, null, "Thread-" + nextThreadNum(), 0);
}
6.3 守护线程
作用:给用户线程(我们自己编写的线程)提供服务
特性:
- 线程类型默认继承自父线程(守护线程创建的线程就是守护线程,用户线程创建的线程就是用户线程)
- 通常所有的守护线程都是被JVM自动启动,不是用户启动的。JVM启动的线程除了main线程是非守护线程,其他都是守护线程
- 不影响JVM退出。JVM而言,想要退出只看用户线程不看守护线程。比如说,如果所有的用户线程全部退出但还有三个守护线程没有结束,JVM也会自动退出。守护线程直接对JVM说:“老大,你不需要等我退出”
守护线程和普通线程的区别:💟
- 整体上没有太大的区别。虽然他们名字不同,但是他们都是线程,都在执行代码。只不过他们代码的任务不同。
- 唯一的区别就是在于是否影响JVM的离开。用户线程会影响,而守护线程不会。
- 他们的作用也不同:用户线程是执行我们逻辑的,守护线程是服务于我们的
6.4 面试题
守护线程和普通线程的区别
我们是否需要给线程设置为守护线程?
首先不是需不需要而是应不应该。我们不应该将自己的线程设置为守护线程。一旦设置成守护线程会变得非常危险。比如,我们这个线程是用来访问文件的,一旦设置成守护线程,有可能发生线程还在访问文件JVM就关了的现象。导致线程被强行停止,如果线程在改变数据,就会导致数据不一致。我们自己开发过程中没有必要设置为守护线程。因为JVM提供的守护线程已经足够服务于我们了
6.5 线程优先级
10个级别,默认是5。子线程如果没有指定优先级,默认继承父线程的
public static final int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public static final int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public static final int MAX_PRIORITY = 10;
注意:我们的程序设计不应依赖于优先级
- 不同OS不一样:Java会将优先级映射到OS中的优先级。我们Java优先级是10,但是windowsOS中只有7个
- 优先级会被OS改变:哪怕程序始终运行在windowsOS中,OS中有一个特定的线程推进器,它会自动执行努力线程不管他的优先级多少。所以,间接的改变了优先级。
- 线程可能出现被饿死的现象:如果这个线程的优先级设置过低
总结:
6.6 面试题
什么时候我们需要设置守护线程?
首先不是需不需要而是应不应该。==我们不应该将自己的线程设置为守护线程。==一旦设置成守护线程会变得非常危险。比如,我们这个线程是用来访问文件的,一旦设置成守护线程,有可能发生线程还在访问文件JVM就关了的现象。导致线程被强行停止,如果线程在改变数据,就会导致数据不一致。我们自己开发过程中没有必要设置为守护线程。因为JVM提供的守护线程已经足够服务于我们了
我们应该如何应用线程优先级来帮助程序运行?有哪些禁忌?
我们不应该使用优先级来帮助程序运行
- 不同OS不一样:Java会将优先级映射到OS中的优先级。我们Java优先级是10,但是windowsOS中只有7个
- 优先级会被OS改变:哪怕程序始终运行在windowsOS中,OS中有一个特定的线程推进器,它会自动执行努力线程不管他的优先级多少。所以,间接的改变了优先级。
- 线程可能出现被饿死的现象:如果这个线程的优先级设置过低