六. 线程属性
线程各属性纵览
ID
每次程序运行唯一、自增、不可被修改
代码
public static void main(String[] args) throws IOException {
Thread main = Thread.currentThread();
System.out.println(main.getName() + ">>" + Thread.currentThread().getId());
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ">>" + Thread.currentThread().getId());
},
"println").start();
System.in.read();
}
//java.lang.Thread#nextThreadID
//全局自增,因为是++所以从1开始
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
println睡眠时
printlln关闭后
可以看到除了VM线程,只剩下11个了,这些线程除main外都是是JVM启动时创建的,用于GC,代码编译,finalizer方法执行等等处理.
name
用于更清晰的判断线程的功能(给程序员和用户使用,使得在开发和调试过程中,更容易分辨线程的功能),JVM并不限制存在相同名称的线程.
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
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;
...
}
public final String getName() {
return name;
}
可以看到默认情况下是通过"Thread-"加上当前线程ID.
如果自定义线程名称,通过set方法,当status!=0也就是运行起来后,还会通过native方法给运行的线程名称更新值.
//java.lang.Thread#setName
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);
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "sleep");
System.out.println(thread.getName());
thread.start();
TimeUnit.SECONDS.sleep(5);
thread.setName("sleep+0");
System.out.println(thread.getName());
}
sleep
sleep+0
daemon
给用户线程(默认new thread)提供服务,当系统中只存在守护线程时,JVM会直接关闭守护线程并退出.
在线程start前,可以通过thread.setDaemon设置
/* Whether or not the thread is a daemon thread. */
private boolean daemon = false;
/*
Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads.
This method must be invoked before the thread is started.
*/
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">>"+LocalTime.now());
},"sleep");
thread.setDaemon(true);
System.out.println(Thread.currentThread().getName()+">>"+LocalTime.now());
thread.start();
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+">>"+LocalTime.now());
}
thread.setDaemon(true);
main>>20:47:30.435
main>>20:47:35.442
thread.setDaemon(false);//默认为false
main>>20:48:07.395
main>>20:48:12.397
sleep>>20:48:17.402
程序启动自带的一些线程都是守护线程,用于给main线程和之后new的daemon为false的线程提供服务.
//java.lang.ref.Reference
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
//守护线程
handler.setDaemon(true);
handler.start();
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
public boolean tryHandlePendingReference() {
return tryHandlePending(false);
}
});
}
//java.lang.ref.Finalizer
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
//守护线程
finalizer.setDaemon(true);
finalizer.start();
}
守护线程创建的线程默认就是守护线程
java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean){
...
Thread parent = currentThread();
...
this.daemon = parent.isDaemon();
...
}
priority
作用:希望某个线程多运行
默认为5,如果存在父线程,跟随父线程.
//java.lang.Thread
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean){
...
this.priority = parent.getPriority();
...
}
程序的设置不应该依赖优先级
因为不同的OS优先级不一样
java中有10个优先级
windows只有7个优先级
linux会忽略,所有线程优先级都是一样的
Solaris有2的32次方个优先级.
这样,优先级的设置就是不可靠的,依赖于优先级的程序也将是不可靠的
优先级会被OS改变
哪怕一直运行在一个OS中,优先级也可能被OS修改.
例如在Windows中有个优先级推进器的功能,当它发现某个线程特别想执行时,会越过优先级的设置去为这个线程分配时间片
问题
什么时候需要设置守护线程
应用程序一般不需要设置守护线程.只有当这个线程属于服务于其它线程,且是否运行完毕都无所谓的情况下,才设置为守护线程
我们应该如何应用线程优先级来帮助程序运行?有那些禁忌?
不应该通过优先级帮助程序运行,因为不同的OS对优先级处理方式不同
不同的操作系统如何处理优先级问题?
java中有10个优先级
windows只有7个优先级
linux会忽略,所有线程优先级都是一样的
Solaris有2的32次方个优先级.
这样,优先级的设置就是不可靠的,依赖于优先级的程序也将是不可靠的
优先级会被OS改变
哪怕一直运行在一个OS中,优先级也可能被OS修改.
例如在Windows中有个优先级推进器的功能,大致作用是当系统发现一个线程执行的特别频繁时,可能会越过线程优先级去为它分配执行时间,从而减少因为线程频繁切换而带来的性能损耗。