Java 多线程(三):T1、T2、T3 执行顺序面试题
一、阻塞主线程的方式:
public class T123 { public static void main(String[] args) throws InterruptedException { final Thread thread1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } } }, "T1"); thread1.start(); thread1.join(); final Thread thread2 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } } }, "T2"); thread2.start(); thread2.join(); Thread thread3 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } } }, "T3"); thread3.start(); thread3.join(); } }
二、阻塞子线程的方式:
public class T123 { public static void main(String[] args) { final Thread thread1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } } }, "T1"); thread1.start(); final Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } } }, "T2"); thread2.start(); Thread thread3 = new Thread(new Runnable() { @Override public void run() { try { thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "--->" + i); } } }, "T3"); thread3.start(); } }
三、Thread.join 的作用和原理
Thread.join的含义是让当前线程等待PreviousThread线程终止之后才从thread.join返回。简单来说,就是PreviousThread线程没有执行完之前,当前线程会一直阻塞在join方法处。
Thread.join实现原理:
探究Thread.join的实现原理,我们可以看一下其方法内部的代码:
public class Thread implements Runnable { ... public final void join() throws InterruptedException { join(0); } ... 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) { //判断是否携带阻塞的超时时间,等于0表示没有设置超时时间 while (isAlive()) {//isAlive获取线程状态,无线等待直到previousThread线程结束 wait(0); //调用Object中的wait方法实现线程的阻塞 } } else { //阻塞直到超时 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } ...
从 join方法的源码来看,join方法的本质调用的是 Object中的wait 方法实现线程的阻塞,调用wait方法必须要获取锁,所以join方法是被synchronized修饰的,synchronized修饰在方法层面相当于synchronized(this),this就是 previousThread本身的实例。
为什么previousThread线程执行完毕就能够唤醒住线程呢?或者说是在什么时候唤醒的?
首先,我们知道通过wait方法阻塞的线程,需要通过notify或者notifyAll来唤醒,所以线程执行完毕后,肯定会有一个唤醒的操作,而唤醒的代码,我们并没有在Java层找到,当我们查询native层的代码的时候,我们可以找到 thread.cpp,我们来看一下在线程退出的时候,有没有做相关的唤醒操作:
void JavaThread::exit(bool destroy_vm, ExitType exit_type) { assert(this == JavaThread::current(), "thread consistency check"); ... // 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); assert(!this->has_pending_exception(), "ensure_join should have cleared"); ...
看一下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 //这里是清除native线程,这个操作会导致isAlive()方法返回false java_lang_Thread::set_thread(threadObj(), NULL); lock.notify_all(thread);//注意这里 // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); }
ensure_join方法中,调用 lock.notify_all(thread); 唤醒所有等待thread锁的线程,意味着调用了join方法被阻塞的主线程会被唤醒; 到目前为止,我们基本上对join的原理做了一个比较详细的分析
总结,Thread.join其实底层是通过wait/notifyall来实现线程的通信达到线程阻塞的目的;当线程执行结束以后,会触发两个事情,第一个是设置native线程对象为null、第二个是通过notifyall方法,让等待在previousThread对象锁上的wait方法被唤醒。

浙公网安备 33010602011771号