多线程3-线程间通信

线程间通信

等待/通知机制(wait / notify)

- 这两种方法都要在同步代码块或同步方法中调用。
- 都需要先获得对象级别的锁。
- 只有两个方法的对象锁一致,即“对象监视器”一致,再能通过notify方法通知到执行wait方法的线程继续执行。
- 使用wait方法后,该线程会释放对象锁,并进入阻塞队列等待被唤醒。
- notify方法唤醒wait方法后,该线程进入就绪队列,但是要等待notify方法的线程将synchronized代码块全部运行完才能释放对象锁,使被唤醒的线程运行。即运行notify方法后不会立即释放对象锁。
- 不能interrupt已经被wait的线程,因为该线程已经释放锁并且进入阻塞队列了。
- notify一次只能唤醒一个被阻塞的线程。
- 如果存在同一个“对象监视器”的多个线程被wait,那么nitify的时候是随机唤醒一个线程。如果要全部唤醒,使用notifyAll。
- wait还有带long类型参数的使用方法。该方法时等待某一时间内是否有线程被唤醒,如果没有,则自动唤醒线程。

比较sleep方法和wait(long)方法。

线程A和线程B分别调用对象C的同步方法X和Y,方法X设置wait(5000),方法Y设置wait(3000),运行程序。线程A在运行方法X的wait 时释放对象锁,线程A进入阻塞队列等待被唤醒,线程B获得对象锁,同样运行到wait 时释放对象锁,线程B进入阻塞队列等待被唤醒。等待3秒后,线程B被自动唤醒,运行wait方法之后的代码,线程跑完之后释放锁。程序再等待2秒后(共等待5秒)线程A被自动唤醒,其他同上。即程序运行的总时长为5秒。如果将wait替换成sleep,因为同步性,共需8秒。
若此时有一个notify方法唤醒线程,随机一个线程会被立即唤醒,而其他阻塞队列中的线程需要等待特定时间后才能被自动唤醒或者等待其他notify方法唤醒。
若多个线程在设置wait(long)方法后,同时被自动唤醒,则同步执行,即排队执行,顺序随机。

通知过早

即线程还没有运行wait方法进入阻塞队列就被notify方法去唤醒。

生产者-消费者模式

  • 一对一可以实现,生产者确认当前是否存在X,不存在则生产X并通知消费者(唤醒线程),已存在则wait;消费者确认是否存在X,已存在则消费并通知生产者(唤醒线程),不存在则wait。
  • 多对多出现假死。由于notify是随机唤醒受同一个对象监视器监视的多个线程中的一个。也就是说生产者生产之后,唤醒的如果是另一个生产者线程,该线程就进入wait状态,反复下去,所有现成都进入waiting状态。
  • 如何解决假死:每次唤醒时,不再随机唤醒一个线程,而是去唤醒所有线程。

多线程中的判断条件,使用if会出错,建议使用while。
原因:wait的线程在被唤醒之后,会从wait语句之后继续执行。使用if就不会在判断条件,使用while还会再去判断。

join方法的使用

当主线程调用其他线程进行运算等操作时,若需要等待该线程的结果返回后才继续主线程之后的操作,就需要用到join方法。

方法join()的作用是等待线程对象的销毁。

在主线程中调用线程B的join()方法,主线程中join方法之后的模块将等待线程B执行完之后才会执行。
join原理是⑩主线程进入阻塞状态,等待其他线程执行完销毁后唤醒。

比较synchronized和join的同步运行

- join类似在内部使用了wait方法,当线程销毁后又唤醒。
- synchronized使用的是对象监视器原理,即锁机制。 

join和interrupt同时使用有异常

join也可带参,形如:join(long),意为等待long长度的时间。

join(long)和sleep(long)的区别

join方法底层是使用wait方法实现的,所以具有释放锁的特点,而sleep不会释放锁。

ThreadLocal类的使用

ThreadLocal保证了各自线程之间私有数据的隔离性。

InheritableThreadLocal类的使用

使子线程可以从父线程中取得值。
注意:如果子线程取得值的同时,父线程改变该值,则子线程中的值任然是旧值。

猜你喜欢

转载自blog.csdn.net/weixin_39389850/article/details/80336341
今日推荐