Java中线程通信协作的最常见的两种方式:
一.syncrhoized加锁的线程的Object类的wait()/notify()/notifyAll()
二.ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()
Java是通过Object类的wait()、notify()、notifyAll()这几个方法来实现线程间通信的。

  • wait():告诉当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用notify()为止。
  • notify():唤醒同一对象监视器中调用wait()的第一个线程。这类似排队买票,一个人买完之后,才能让后面的人买。
  • notifyAll():醒同一对象监视器中调用wait()的所有线程,具有最高优先级的线程首先被唤醒并执行。

wait()、notify()、notifyAll()这三个方法只能在synchronized方法中调用,即无论线程调用一个对象的wait还是notify方法,该线程必须先得到该对象的锁标记。这样notify就只能唤醒同一对象监视器中调用wait的线程。而使用多个对象监视器,就可以分别有多个wait、notify的情况,同组理的wait只能被同组的notify唤醒。

image.png

join()

在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。

对于以下代码,虽然 b 线程先启动,但是因为在 b 线程中调用了 a 线程的 join() 方法,b 线程会等待 a 线程结束才继续执行,因此最后能够保证 a 线程的输出先于 b 线程的输出。

  1. public class JoinExample {
  2. private class A extends Thread {
  3. @Override
  4. public void run() {
  5. System.out.println("A");
  6. }
  7. }
  8. private class B extends Thread {
  9. private A a;
  10. B(A a) {
  11. this.a = a;
  12. }
  13. @Override
  14. public void run() {
  15. try {
  16. a.join();
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("B");
  21. }
  22. }
  23. public void test() {
  24. A a = new A();
  25. B b = new B(a);
  26. b.start();
  27. a.start();
  28. }
  29. }
  30. public static void main(String[] args) {
  31. JoinExample example = new JoinExample();
  32. example.test();
  33. }
  34. /*
  35. A
  36. B
  37. */

wait() notify() notifyAll()

调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。

它们都属于 Object 的一部分,而不属于 Thread。

只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateException。

使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。

  1. public class WaitNotifyExample {
  2. public synchronized void before() {
  3. System.out.println("before");
  4. notifyAll();
  5. }
  6. public synchronized void after() {
  7. try {
  8. wait();
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. System.out.println("after");
  13. }
  14. }
  15. public static void main(String[] args) {
  16. ExecutorService executorService = Executors.newCachedThreadPool();
  17. WaitNotifyExample example = new WaitNotifyExample();
  18. executorService.execute(() -> example.after());
  19. executorService.execute(() -> example.before());
  20. }
  21. /*
  22. before
  23. after
  24. */

await() signal() signalAll()

java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。

相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。

使用 Lock 来获取一个 Condition 对象。

  1. public class AwaitSignalExample {
  2. private Lock lock = new ReentrantLock();
  3. private Condition condition = lock.newCondition();
  4. public void before() {
  5. lock.lock();
  6. try {
  7. System.out.println("before");
  8. condition.signalAll();
  9. } finally {
  10. lock.unlock();
  11. }
  12. }
  13. public void after() {
  14. lock.lock();
  15. try {
  16. condition.await();
  17. System.out.println("after");
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  24. }
  25. public static void main(String[] args) {
  26. ExecutorService executorService = Executors.newCachedThreadPool();
  27. AwaitSignalExample example = new AwaitSignalExample();
  28. executorService.execute(() -> example.after());
  29. executorService.execute(() -> example.before());
  30. }
  31. /*
  32. before
  33. after
  34. */