线程Thread API分析
我们在创建线程的时候 建议同时需要给当前线程起一个名字,为线程赋予一个有意义的名字,有助于我们排查问题,
线程的默认命名
Thread();
Thread(Runnable target);
Thread(ThreadGroup group,Runnable target);
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
我们有Thread.java类看出 如果我们没有给当前线程命名,那么线程将会以"Thread-"作为前缀与一个数字进行组合,这个自增的数字在整个JVM进程会不断增加。
那么,我们需要给当前线程命名:
Thread(Runnable target,String name);
Thread(String name);
Thread(ThreadGroup threadGroup,Runnable runnable,String name);
Thread(ThreadGroup threadGroup,Runnable runnable,String name,long stackSize);
Thread(ThreadGroup group,String name);
//给当前thread命名
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
if (threadStatus != 0) {//如果线程不是new状态 则不会执行下面代码
setNativeName(name);
}
}
不论你使用的是默认的名字,还是自定义名字,如果一个线程启动,那么线程的名字,将不能被修改。从源代码可以看出。
线程的父子关系
Thread的所有构造函数,都会去执行init方法
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();
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();
}
有上述代码可以看出来 currentThread() 是获取当前线程,我们new出Thread ,没有执行start之前,他只能代表当前线程的一个事例,由此我们可以推出,currentThread()方法得到的应该是父线程。
- 一个线程肯定是由另一个线程创建的。
- 被创建的线程肯定是它的父线程。
ThreadGroup 和Thread
在构造Thread的时候,可以显式的将当前线程加入一个线程组中,如果在构造Thread的时候并没有显式的指定一个ThreadGroup,那么他会加入父线程所在的线程组,
- main线程所在的ThreadGroup称为main
Thread和Runnable
Thread负责线程本身的职责相关和控制,而Runnable则负责逻辑单元的部分。符合类的单一职责原则。
堆内存不变,栈内存越大,可创建的线程数量越小
占内存不变,堆内存越大,可创建的线程数量越小
线程数量=(最大地址空间-JVM堆内存-ReservedOsMemory)/ThreadStackSize(Xss);
守护线程
概念:守护线程是一个特殊的线程,一般用于后台处理工作,
特点:调用setDaemon方法即可,true代表守护线程,如果父线程是守护线程,则子线程也是正常线程,setDaemon只有在线程启动之前设置才能生效,
作用:如果JVM没有一个非守护线程,那么JVM会退出,非守护线程没有生命周期,如果main方法完成了工作,则JVM无法退出,因为还有守护线程(垃圾回收线程还在工作)。
public void main(String [] args){
Thread thread = new Thread(){
while(true){
sleep(1);
}
}
}
thread.setDaemon(true);
thread.start();
Thread.sleep(200);
System.out.println("main threadLife end ");
总结
我们了解了Thread的构造函数,也介绍了线程的父子关系,以及总父线程哪里继承了优先级,守护线程,和ThreadGroup等特性。