定义

Guarded Suspension,用在一个线程等待另一个线程的执行结果

要点

  • 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
  • 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
  • JDK 中,join 的实现Future 的实现,采用的就是此模式
  • 因为要等待另一方的结果,因此归类到同步模式

同步模式之保护性暂停(join原理) - 图1
join示例:

  1. public static void main(String[] args){
  2. Thread t = new Thread(()->{
  3. Thread.sleep(1000);
  4. });
  5. t.join();
  6. System.out.println("1000");
  7. }

Future示例:

  1. public static void main(String[] args) throws ExecutionException, InterruptedException {
  2. // 创建任务对象
  3. FutureTask<Integer> task3 = new FutureTask<>(() -> {
  4. System.out.println("hello");
  5. return 100;
  6. });
  7. // 主线程阻塞,同步等待 task 执行完毕的结果
  8. Integer result = task3.get();
  9. System.out.println("结果是:"+result);
  10. }

join()的原理

  1. // 无参数的join,调用join(0)
  2. public final void join() throws InterruptedException {
  3. join(0);
  4. }
  5. public final synchronized void join(long millis)
  6. throws InterruptedException {
  7. long base = System.currentTimeMillis();
  8. long now = 0;
  9. if (millis < 0) {
  10. throw new IllegalArgumentException("timeout value is negative");
  11. }
  12. // 无参的join方法会调用join(0),从而进入该分支
  13. if (millis == 0) {
  14. // 判断线程是否还存活,存活则调用wait等待
  15. while (isAlive()) {
  16. wait(0);
  17. }
  18. } else {
  19. // 判断线程是否还存活
  20. while (isAlive()) {
  21. // 计算剩余时间
  22. long delay = millis - now;
  23. // <=0 表示join等待已经超时,退出等待
  24. if (delay <= 0) {
  25. break;
  26. }
  27. // 使用带有时间的wait方法等待
  28. wait(delay);
  29. // 计算已过去的时间
  30. now = System.currentTimeMillis() - base;
  31. }
  32. }
  33. }
  34. // native方法,它会使线程进入等待,直到通过notify唤醒或者超时
  35. public final native void wait(long timeout) throws InterruptedException;
  36. public final native boolean isAlive();

通过阅读源码我们发现,如果我们调用无参数的join其实是调用了join(0),所以我们直接看有参数的join方法。
该方法分为两个分支,一个是millis=0,即无限时等待。另一个是millis>0,即限时等待
同步模式之保护性暂停(join原理) - 图2

  1. /**
  2. * TestGuardedObject
  3. * <p>
  4. * join原理
  5. * </p>
  6. * encoding:UTF-8
  7. *
  8. * @author lisonglin 23:36 2022/3/4
  9. */
  10. public class TestGuardedObject {
  11. public static void main(String[] args) {
  12. GuardedObject v2 = new GuardedObject();
  13. new Thread(() -> {
  14. try {
  15. Thread.sleep(1);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. v2.complete(null);
  20. try {
  21. Thread.sleep(1);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. v2.complete(Arrays.asList("a", "b", "c"));
  26. }).start();
  27. Object response = v2.get(2500);
  28. if (response != null) {
  29. System.out.println("get response: "+((List<String>) response).size()+"lines");
  30. } else {
  31. System.out.println("can't get response");
  32. }
  33. }
  34. }
  35. class GuardedObject {
  36. private Object response;
  37. private final Object lock = new Object();
  38. public Object get(){
  39. return get(0);
  40. }
  41. public Object get(long millis) {
  42. synchronized (lock) {
  43. long base = System.currentTimeMillis();
  44. long now = 0;
  45. if (millis == 0) {
  46. while (response == null) {
  47. try {
  48. lock.wait();
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. } else {
  54. while (response == null) {
  55. long delay = millis - now;
  56. System.out.println("waitTime:" + delay);
  57. if (delay <= 0) {
  58. System.out.println("break...");
  59. break;
  60. }
  61. try {
  62. lock.wait(delay);
  63. } catch (InterruptedException e) {
  64. e.printStackTrace();
  65. }
  66. now = System.currentTimeMillis() - base;
  67. System.out.println("timePassed:" + now + ", object is null");
  68. }
  69. }
  70. return response;
  71. }
  72. }
  73. public void complete(Object response) {
  74. synchronized (lock) {
  75. this.response = response;
  76. System.out.println("notify...");
  77. lock.notifyAll();
  78. }
  79. }
  80. }

waitTime:2500 notify... timePassed:1, object is null waitTime:2499 notify... timePassed:3, object is null get response: 3lines