wait:Object类的方法
1、作用:使其执行线程生命状态变为WAITING,使其被暂停,可以实现等待。等待之后,只有这个对象又调用了notify()/notifyAll()方法之后,这个等待线程被唤醒。
2、需要持有对象someObject的内部锁才能执行,所以对象someObject.wait()方法需要在该对象所引导的临界区内。
保护条件、someObject.wait()、目标动作都应该在临界区内。
保护条件的判断和someObject.wait()方法应该在一个循环方法内。
3、伪代码:
public synchronized void sss() { while(条件){ wait(); } doAction(); }
4、注意:wait是让当前执行线程暂停。
等待线程:wait的执行线程,也就是被暂停的线程,就称为对象someObject上的等待线程。
(1)释放someObject对象的内部锁(为了让notify的线程获取当前对象的内部锁)
(2)然后将当前线程加入到与这个对象相关的等待集Wait Set(java虚拟机会为每一个对象都维护一个等待集,用来存储这个对象上的等待线程)
(3)wait是原子操作的方法,当前线程被暂停后就不往下执行,等待被唤醒,然后重新获得对象的内部锁,再重新判断条件往下执行。如果条件又不符合,还可能又进入wait。直到条件成立,执行完目标动作这个方法才算完成。
notify/notifyAll:Object类的方法
1、同样需要持有对象someObject的内部锁才能执行,notify/notifyAll方法需要在该对象所引导的临界区内。
2、作用:唤醒线程
notify是唤醒该对象相关的wait set的任意一个线程,notifyAll是唤醒相关的所有线程。
3、伪代码
synchronized void sss() { doUpdate(); someObject.notify(); // someObject.notifyAll(); }
通知线程:notify/notifyAll所在的线程叫做通知线程
4、当前线程执行notify()/notifyAll()后:
(1)执行notify之后,通知唤醒该对象相关的一个线程(notifyAll线程唤醒相关的所有线程),但此时还没释放对象的锁,所以其他线程只能等待。等到线程收到通知之后,没申请到对象的锁,此时还待在wait set等待集中。
(2)直到这个临界区执行完,再释放对象的内部锁
(3)等待线程收到通知,并申请获得对象的锁之后,该等待线程会从该对象的wait set等待集中移除。
5、注意:
尽量让notify靠近临界区结束的地方。免得等待线程因为没有获得对象的锁,而又进入等待状态。
join:Thread类的方法
1、join的作用:
这句代码执行后会阻塞代码所在的线程。意思是哪个线程执行这句代码,哪个线程就被阻塞。
需要的锁:是子线程对象的内部锁
阻塞的线程:当前的执行线程,也就是主线程。
join阻塞主线程就是通过wait和notifyAll实现的。
2、以例子说明join阻塞和结束阻塞过程:
上面是join如何阻塞主线程的说明。
被阻塞的主线程是如何被唤醒的呢?
当子线程执行完run方法之后,底层在jvm源码里,会执行线程的exit方法,里面会调用notifyAll方法
void JavaThread::exit(booldestroy_vm,ExitTypeexit_type);