什么时候要用join()方法?
1,join方法是Thread类中的方法,主线程执行完start()方法,线程就进入就绪状态,虚拟机最终会执行run方法进入运行状态.此时.主线程跳出start方法往下执行
2,两个线程以上,当一个线程需要另一个线程执行的结果时,可以在该线程之前调用另一个线程对象的join方法,如下:
public class TestThread { public static void main(String[] args) throws InterruptedException { Thread t1 = new ThreadA("ThreadA"); Thread t2 = new Thread(new ThreadB(),"ThreadB"); t1.start();//主线程调用start方法,开启t1线程, t1.join();//主线程调用join方法,获取对象t1对象锁,挂起主线程.直到t1线程结束 t2.start();//主线程调用start方法,开启t2线程 t2.join();//主线程调用join,获取t2对象锁,挂起主线程,直到t2线程结束 System.out.println(Thread.currentThread().getName()+"主线程结束"); } }
如上代码,主线程一路下来调用其他线程的对象的join方法,就会被挂起直到该线程对象所在的线程结束.从而实现线程的顺序执行.
查看join的源码,该方法是同步方法,锁是t1线程所在对象的对象锁,我们可以看到,wait总是在synchronized代码块里面使用,并且在while循环中,有wait()就必然有notify()/notifyAll(),所以同样的一般情况下notify()/notifyAll()也是在synchronized代码块中的while使用的,synchronized,while,wait,notify结合可以实现经典的生产者消费者模式.
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); .... //主线程调用join方法就拥有了t1对象的锁,wait(0)一直处于等待状态, //直到某个线程调用了该对象的notify或者notifyAll() if (millis == 0) { while (isAlive()) { wait(0); } } else { .... } }
在join方法中我们看到了wait(),那么一定在某个地方有notify方法,该方法则在jvm源码中,这个段源码的作用是结束线程,在线程退出前需要做一些动作,其中就有调用notify_all的动作,看注释,还将isAlive变为false,
// 位于/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); // 唤醒其他线程 lock.notify_all(thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); }
由此我们即看到了notify,也看到了isAlive变为false,所以主线程再挂起的时候,会等待该对象线程t1结束并调用notify,唤醒之后则跳出循环检测isAlive为false则跳
出循环结束join方法,这就实现了线程的顺序执行,
码解读完毕
从代码中的while(isAlive){wait()}说起,
如果主线程在等待状态,被其他线程唤醒呢,所以前文我说,notify也一般配合synchronized,while使用,这个一般情况下是消费者生产者模式,但是在jvm源码中并没
有这么用,这是要等待t1线程结束前调用,所以这个isAlive就是保证一定被t1线程结束唤醒,而其他线程唤醒while判断之后再次进入挂起状态.
几个问题的辨析,
1,线程之间的竞争是同一个对象锁的竞争或者同一个类锁的竞争,与其他对象的锁无关.其他线程抢别的对象的锁,执行别对象的wait/notify方法都与竞争本对象
锁的线程无关.
2, 锁分为对象锁,类锁,锁的使用者叫线程,锁拦截的对象也是线程,不是什么方法,或者其他,.
对象锁,表示对象里面所有被synchronized(this)修饰的代码块和synchronized 修饰的普通方法块,不能同时被两个线程执行.详情见:synchronized的使用方法和作用域:
https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd
类锁是被synchronized(MyClass.class)和static synchronized 修饰的静态方法,不能同时被两个线程执行,详情见:synchronized的使用方法和作用域
https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd
3,线程释放锁的场景,
a,执行完同步代码块,
b,在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放
c在执行同步代码块的过程中,执行了锁所属对象的wait方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒
notify和notifyAll的区别
我们首先得明确,一个对象只有一个锁,参与该对象锁竞争的线程,与参与其他对象锁竞争的线程无关.线程竞争,对应一个对象的一个锁.
有n个线程参与竞争同一对象的锁,t1率先抢到了锁,锁池中有n-1个线程在等待竞争锁,t1调用了wait()方法t1线程挂起,等待池中有一线程等待被唤醒.t2线程,获得了锁,锁池中有n-2个线程等待竞争锁,t2线程调用了wait方法挂起,等待池中有两个线程等待被唤醒,
此时t3线程获得了锁,
如果t3线程在同步代码块中调用的是notify则只会随机唤醒等待池中的一个线程移入锁池中参与锁竞争,有可能是t1,也有可能是t2,等待池中的线程为1
如果t3线程在同步代码块中调用的notifyAll,则会同时唤醒t1,t2移入锁池中参与竞争,等待池中的线程为零,
公众号: