多线程(一):详细描述wait、notify/notifyAll、join及底层实现

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上的等待线程

对象的wait方法可能被不同的线程执行,所以同一个对象可能会有多个等待线程。
5、当前线程执行someObject.wait()之后:

(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)



猜你喜欢

转载自blog.csdn.net/qq_20245089/article/details/80736181