之前的文章中,提到了线程的一个关键方法join,以及Thread的线程状态,这些都是Thread代码中的内容,今天,我们再来看一看Thread。
Thread这个类,可以说是JDK中最重要的类之一,只要你用到了异步、锁、等待等都离不开Thread的身影,哪怕你启动一个main方法,后台也是要用到Thread这个类的。想要了解这个类,还是要从它的构造方法开始。我们来看下Thread的构造方法。
/*无参构造方法*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/*Runnable入参构造方法*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
/*ThreadGroup ,Runnable入参构造方法*/
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
/*线程名称入参构造方法*/
public Thread(String name) {
init(null, null, name, 0);
}
/*ThreadGroup ,线程名称入参构造方法*/
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
/*Runnable ,线程名称入参构造方法*/
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
/*ThreadGroup ,Runnable ,线程名称入参构造方法*/
public Thread(ThreadGroup group, Runnable target, String name) {
init(group, target, name, 0);
}
/*ThreadGroup ,Runnable ,线程名称,预期堆栈大小 入参构造方法*/
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
Thread的构造方法有很多,但是大家可以发现,所有的构造方法都是指向了init方法,Thread的构造方法其实也就是通过init方法来实现的!一般来说,在构造方法中处理逻辑是不太建议的,当然,如果你比较精通这方面,也是可以这么操作的,^_^。
我们来看下init方法。方法里涉及到很多东西,比如权限,线程组,执行的先后顺序,甚至线程占有多大内存都有设置,不过对我们来说,一般用到的就三个线程名称,线程id,以及线程执行的target(Runnable)再加上线程的状态 (*^▽^*)
// Thread 初始化方法
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是一个允许应用实现一种安全策略的类,它允许一个应用去明确,在执行一个可能
//安全或者敏感的操作之前,此操作是否允许在一个安全的上下文中执行。应用可以统一或者拒绝执行
//操作。
SecurityManager security = System.getSecurityManager();
//线程组为空
if (g == null) {
//管理策略不为空,就是去从security中获取当前线程的线程组
if (security != null) {
g = security.getThreadGroup();
}
// security没有赋值group,那么久直接使用parent的ThreadGroup
if (g == null) {
g = parent.getThreadGroup();
}
}
//判断线程组是否允许操作(这个源码有点多,就不深入了)
g.checkAccess();
/*
* security 不为空的话,检验Class的管理策略
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
//把线程加入到ThreadGroup组里。
g.addUnstarted();
//线程组
this.group = g;
//是否是守护线程
this.daemon = parent.isDaemon();
//优先级,默认是5,数字越大越先执行,从0开始
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);
//给线程分配的栈大小,这个一般是默认的
this.stackSize = stackSize;
//线程ID
tid = nextThreadID();
}
这是init方法的一些解释,具体就不深入了,里面的东西牵涉的比较多。下面展示下Thread的属性吧,方便大家尽快熟悉线程。
//线程名字
private volatile String name;
//优先级
private int priority;
// 内置的一个线程Thread类
private Thread threadQ;
//JVM中的JavaThread指针
private long eetop;
// 是否单步执行此线程
private boolean single_step;
// 是否是守护线程
private boolean daemon = false;
// 虚拟机状态
private boolean stillborn = false;
// 传进来的对象
private Runnable target;
// 线程的组
private ThreadGroup group;
// 线程的上下文加载器,可以用来加载一些资源
private ClassLoader contextClassLoader;
// 继承的访问控制上下文
private AccessControlContext inheritedAccessControlContext;
// 给匿名线程命名的整数,匿名线程会被命名为Thread-1这类名字,这个int会自增
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
//ThreadLocal的Map用于保存变量副本,不了解的话可以看下之前关于ThreadLocal的讲解
ThreadLocal.ThreadLocalMap threadLocals = null;
// InheritableThreadLocal用于子线程能够拿到父线程往ThreadLocal里设置的值
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//给线程分配的栈大小
private long stackSize;
// 本地线程终止之后的专用状态
private long nativeParkEventPointer;
// 线程ID
private long tid;
// 用于生成线程ID
private static long threadSeqNumber;
// 线程状态
private volatile int threadStatus = 0;
下面主要就是join和线程的状态了,其他许多都是native方法,不太好分析。
这里我再说下join,我们可以看到join方法。
public final synchronized void join(long millis, int nanos)
throws InterruptedException{
// 略
}
是使用synchronized修饰的,所以一旦有线程(thread1)调用join方法,相当于join方法就获取到了监视器锁,这个锁内部的逻辑没有执行完,主方法的逻辑是无法执行的,这个就是join方法能提前运行的。
while (isAlive()) {
wait(0);
}
可以看到,join方法中有wait方法,那么既然有wait方法最终执行完肯定是要唤醒做唤醒操作的,毕竟thread1是在主线程中运行的,不能thread1运行完了,啥都不管了,然后就把主线程撂那了,那主线程要骂N了。这里copy下大佬的带讲解
//一个c++函数:
void JavaThread::exit(bool destroy_vm, ExitType exit_type) ;
//这家伙是啥,就是一个线程执行完毕之后,jvm会做的事,做清理啊收尾工作,
//里面有一个贼不起眼的一行代码,眼神不好还看不到的呢,就是这个:
ensure_join(this);
//翻译成中文叫 确保_join(这个);代码如下:
static void ensure_join(JavaThread* thread) {
Handle threadObj(thread, thread->threadObj());
ObjectLocker lock(threadObj, thread);
thread->clear_pending_exception();
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
java_lang_Thread::set_thread(threadObj(), NULL);
//同志们看到了没,别的不用看,就看这一句,
//thread就是当前线程
lock.notify_all(thread);
thread->clear_pending_exception();
}
// 链接:https://www.zhihu.com/question/44621343/answer/97640972
这里可以看到在JVM源码中有对线程的唤醒操作,就是thread1执行完了,肯定是要notifyAll一下的,然后唤醒主线程,不然逻辑就卡那了,O(∩_∩)O哈哈~
No sacrifice ,no victory~