线程生命周期
1. 线程的生命周期包括新建、就绪、运行、阻塞、销毁五中状态
-
新建:使用new方法,实例化一个线程
Thread thread = new Thread();
-
就绪:调用start() 方法后,这时候线程处于等待CPU分配资源阶段
调用yeild()方法,让出cpu资源,程序由运行状态变为就绪状态
当阻塞的状态通过notify()和notifyAll()方法唤醒以后
thread.start();
-
运行:当就绪的线程获取到CUP资源以后, 便进入运行状态
-
阻塞:在运行状态的时候,由于某些原因使线程变成阻塞状态,比如sleep()、wait()、join()
- sleep() : sleep 状态下线程并不会释放锁
- wait(): 处于wait状态下的线程会释放自己获取的锁
- join():当线程调用join方法后,线程底层会调用wait()进入阻塞, join底层是用wait()和notify()实现的,待其他线程调用notify()或notifyAll(),线程会进入到就绪状态抢占cpu资源
-
销毁:如果线程在正常执行结束或者线程受到强制终止或者受到异常而导致结束,那么线程就会被销毁
2. 这里面有几个方法需要重点解析
-
yield()
- 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。
- 用了yield方法后,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
- 通过yield方法来实现两个线程的交替执行。不过请注意:这种交替并不一定能得到保证。
- yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会
-
join()
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将可能早于子线程结束。如果主线程需要知道子线程的执行结果时,就需要等待子线程执行结束了。主线程可以sleep()进入休眠,但休眠时间不好确定,因为子线程的执行时间不确定,join()方法比较合适这个场景。
3. join调用方式如下所示:
Thread subThread = new Thread(new Runnable() {
@Override
public void run() {
// do something
}
});
subThread.start();
subThread.join();
// 等子线程 subThread 完成后,主线程被唤醒,接着执行
4. join底层源码如下所示:
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
底层调用了join(long millis)方法:
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
join的底层是调用了线程的wait()方法,当主线程调用了join()方法的时候,主线程会获取子线程 subThread 的对象锁,调用该对象的wait()方法,然后进入阻塞状态
主线程被唤醒:
那么t2是如何被唤醒的呢。因为我们自己使用的时候都是wait和notify/notifyAll成对出现的。否则线程将无限期等待下去。如果等待线程还是一个非守护线程。那么就会导致程序不能正常结束。
// 位于/hotspot/src/share/vm/runtime/thread.cpp中
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
// ...
// Notify waiters on thread object. This has to be done after exit() is called
// on the thread (if the thread is the last thread in a daemon ThreadGroup the
// group should have the destroyed bit set before waiters are notified).
// 有一个贼不起眼的一行代码,就是这行
ensure_join(this);
// ...
}
static void ensure_join(JavaThread* thread) {
// We do not need to grap the Threads_lock, since we are operating on ourself.
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
// Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED.
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
// Clear the native thread instance - this makes isAlive return false and allows the join()
// to complete once we've done the notify_all below
java_lang_Thread::set_thread(threadObj(), NULL);
// 同志们看到了没,别的不用看,就看这一句 thread就是当前线程
lock.notify_all(thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}