同步模式之保护性暂停

一个线程等待另一个线程的执行结果
要点:

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

image.png

死锁

定位死锁

  • 检测死锁可以使用 jconsole 工具,线程tab中有个检测死锁的功能;
  • 使用 jps 定位进程 id,再用 jstack 定位死锁;

    模拟死锁

    ```java package top.simba1949;

/**

  • @author Anthony
  • @date 2020/10/26 16:48 */ public class Application { public static void main(String[] args) {

    1. Object a = new Object();
    2. Object b = new Object();
    3. Thread t1 = new Thread(() ->{
    4. synchronized (a){
    5. System.out.println("lock a");
    6. try {
    7. Thread.sleep(10);
    8. synchronized (b){
    9. System.out.println("lock b");
    10. }
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. }
    15. }, "t1");
    16. Thread t2 = new Thread(() ->{
    17. synchronized (b){
    18. System.out.println("lock b");
    19. try {
    20. Thread.sleep(5);
    21. synchronized (a){
    22. System.out.println("lock a");
    23. }
    24. } catch (InterruptedException e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. } ,"t2");
    29. t1.start();
    30. t2.start();

    } } ```

    同步控制

    顺序控制

    wait&notify实现

    ```java package top.simba1949;

/**

  • @author Anthony
  • @date 2020/10/24 10:01 */ public class Application {

    static final Object lock = new Object(); /**

    • t2 是否运行过 */ static boolean t2RunBl = false;

      public static void main(String[] args) { Thread t1 = new Thread(() -> {

      1. synchronized (lock){
      2. while (!t2RunBl){
      3. try {
      4. // 如果线程2未执行,进入等待状态
      5. lock.wait();
      6. } catch (InterruptedException e) {
      7. e.printStackTrace();
      8. }
      9. }
      10. System.out.println("t1线程");
      11. }

      }, “t1”);

      Thread t2 = new Thread(() -> {

      1. try {
      2. // 使用 sleep 可以放大控制顺序的效果
      3. Thread.sleep(10000);
      4. } catch (InterruptedException e) {
      5. e.printStackTrace();
      6. }
      7. synchronized (lock){
      8. System.out.println("t2线程");
      9. t2RunBl = true;
      10. lock.notify();
      11. }

      }, “t2”);

      t1.start(); t2.start(); } } ```

      ReentrantLock和条件变量实现

      ```java package top.simba1949;

import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock;

/**

  • @author Anthony
  • @date 2020/10/24 10:01 */ public class Application {

    static final ReentrantLock reentrantLock = new ReentrantLock(); static Condition condition = reentrantLock.newCondition(); /**

    • t2 是否运行过 */ static boolean t2RunBl = false;

      public static void main(String[] args) { Thread t1 = new Thread(() -> {

      1. reentrantLock.lock();
      2. try{
      3. while (!t2RunBl){
      4. try {
      5. // 没有获取到锁的线程,进入线程休息室休息,等待唤醒,
      6. // 唤醒后从这条语句后面开始执行
      7. condition.await();
      8. System.out.println("t1");
      9. } catch (InterruptedException e) {
      10. e.printStackTrace();
      11. }
      12. }
      13. }finally {
      14. reentrantLock.unlock();
      15. }

      }, “t1”);

      Thread t2 = new Thread(() -> {

      1. try {
      2. // 使用 sleep 可以放大控制顺序的效果
      3. Thread.sleep(10000);
      4. } catch (InterruptedException e) {
      5. e.printStackTrace();
      6. }
      7. reentrantLock.lock();
      8. try{
      9. System.out.println("t2");
      10. t2RunBl = true;
      11. condition.signal();
      12. }finally {
      13. reentrantLock.unlock();
      14. }

      }, “t2”);

      t1.start(); t2.start(); } } ```

      park&unpark实现

      ```java package top.simba1949;

import java.util.concurrent.locks.LockSupport;

/**

  • @author Anthony
  • @date 2020/10/24 10:01 */ public class Application {

    public static void main(String[] args) {

    1. Thread t1 = new Thread(() -> {
    2. LockSupport.park();
    3. System.out.println("t1");
    4. }, "t1");
    5. t1.start();
    6. Thread t2 = new Thread(() -> {
    7. System.out.println("t2");
    8. LockSupport.unpark(t1);
    9. }, "t2");
    10. t2.start();

    }

}

  1. <a name="aCFuT"></a>
  2. ## 交替输出
  3. <a name="QoNlu"></a>
  4. ### wait&notify实现
  5. ```java
  6. package top.simba1949;
  7. /**
  8. * 输出结果为 a——>b——>c——>a——>b——>c——>...
  9. * 输出内容 等待标记 下一个标记
  10. * a 1 2
  11. * b 2 3
  12. * c 3 1
  13. * @author Anthony
  14. * @date 2020/10/24 10:01
  15. */
  16. public class Application4 {
  17. public static void main(String[] args) {
  18. WaitNotify waitNotify = new WaitNotify(1, 5);
  19. new Thread(() -> waitNotify.print("a", 1, 2), "t1").start();
  20. new Thread(() -> waitNotify.print("b", 2, 3), "t2").start();
  21. new Thread(() -> waitNotify.print("c", 3, 1), "t3").start();
  22. }
  23. }
  24. class WaitNotify{
  25. // 等待标记
  26. private int flag;
  27. // 循环次数
  28. private int loopNumber;
  29. public WaitNotify(int flag, int loopNumber) {
  30. this.flag = flag;
  31. this.loopNumber = loopNumber;
  32. }
  33. /**
  34. * 打印
  35. * 设计思路:
  36. * 如果当前的标记与当前线程等待的标记一致,执行打印内容,并设置当前线程的下一个标记为当前标记,并唤醒其他线程
  37. * 如果当前的标记与当前线程等待的标记不一致,当前线程进入wait状态
  38. * @param str
  39. * @param waitFlag
  40. * @param nextFlag
  41. */
  42. public void print(String str, int waitFlag, int nextFlag){
  43. for (int i = 0; i < loopNumber; i++) {
  44. synchronized (this){
  45. while (flag != waitFlag){
  46. try {
  47. this.wait();
  48. } catch (InterruptedException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. // 打印内容,设置等待标记flag
  53. System.out.print(str + "->");
  54. flag = nextFlag;
  55. this.notifyAll();
  56. }
  57. }
  58. }
  59. }

ReentrantLock和条件变量实现

通过 ReentrantLock 对象实现

  1. package top.simba1949;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * 输出结果为 a——>b——>c——>a——>b——>c——>...
  6. * 输出内容 等待标记 下一个标记
  7. * a 1 2
  8. * b 2 3
  9. * c 3 1
  10. * @author Anthony
  11. * @date 2020/10/24 10:01
  12. */
  13. public class Application6 {
  14. public static void main(String[] args) {
  15. WaitNotify6 waitNotify = new WaitNotify6(1, 10);
  16. new Thread(() -> waitNotify.print("a", 1, 2), "t1").start();
  17. new Thread(() -> waitNotify.print("b", 2, 3), "t2").start();
  18. new Thread(() -> waitNotify.print("c", 3, 1), "t3").start();
  19. }
  20. }
  21. class WaitNotify6 {
  22. private final ReentrantLock reentrantLock = new ReentrantLock();
  23. // 线程休息室
  24. private Condition condition = reentrantLock.newCondition();
  25. // 等待标记
  26. private int flag;
  27. // 循环次数
  28. private int loopNumber;
  29. public WaitNotify6(int flag, int loopNumber) {
  30. this.flag = flag;
  31. this.loopNumber = loopNumber;
  32. }
  33. /**
  34. * 打印
  35. * 设计思路:
  36. * 如果当前的标记与当前线程等待的标记一致,执行打印内容,并设置当前线程的下一个标记为当前标记,并唤醒其他线程
  37. * 如果当前的标记与当前线程等待的标记不一致,当前线程进入wait状态
  38. * @param str
  39. * @param waitFlag
  40. * @param nextFlag
  41. */
  42. public void print(String str, int waitFlag, int nextFlag){
  43. for (int i = 0; i < loopNumber; i++) {
  44. reentrantLock.lock();
  45. while (flag != waitFlag){
  46. try {
  47. // 当前线程进入休息室休息
  48. condition.await();
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. try {
  54. // 打印内容,设置等待标记flag
  55. System.out.print(str + "->");
  56. flag = nextFlag;
  57. condition.signalAll();
  58. }finally {
  59. reentrantLock.unlock();
  60. }
  61. }
  62. }
  63. }

通过继承 ReentrantLock 实现——比较唤醒

  1. package top.simba1949;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * 输出结果为 a——>b——>c——>a——>b——>c——>...
  6. * 输出内容 等待标记 下一个标记
  7. * a 1 2
  8. * b 2 3
  9. * c 3 1
  10. * @author Anthony
  11. * @date 2020/10/24 10:01
  12. */
  13. public class Application5 {
  14. public static void main(String[] args) {
  15. WaitNotify5 waitNotify = new WaitNotify5(1, 10);
  16. new Thread(() -> waitNotify.print("a", 1, 2), "t1").start();
  17. new Thread(() -> waitNotify.print("b", 2, 3), "t2").start();
  18. new Thread(() -> waitNotify.print("c", 3, 1), "t3").start();
  19. }
  20. }
  21. class WaitNotify5 extends ReentrantLock {
  22. private static final long serialVersionUID = -5778347071247661102L;
  23. // 等待队列
  24. private final Condition condition = this.newCondition();
  25. // 等待标记
  26. private int flag;
  27. // 循环次数
  28. private int loopNumber;
  29. public WaitNotify5(int flag, int loopNumber) {
  30. this.flag = flag;
  31. this.loopNumber = loopNumber;
  32. }
  33. /**
  34. * 打印
  35. * 设计思路:
  36. * 如果当前的标记与当前线程等待的标记一致,执行打印内容,并设置当前线程的下一个标记为当前标记,并唤醒其他线程
  37. * 如果当前的标记与当前线程等待的标记不一致,当前线程进入wait状态
  38. * @param str
  39. * @param waitFlag
  40. * @param nextFlag
  41. */
  42. public void print(String str, int waitFlag, int nextFlag){
  43. for (int i = 0; i < loopNumber; i++) {
  44. this.lock();
  45. while (flag != waitFlag){
  46. try {
  47. // 当前线程进入休息室休息
  48. condition.await();
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. try {
  54. // 打印内容,设置等待标记flag
  55. System.out.print(str + "->");
  56. flag = nextFlag;
  57. condition.signalAll();
  58. }finally {
  59. this.unlock();
  60. }
  61. }
  62. }
  63. }

通过继承 ReentrantLock 实现——控制唤醒顺序

  1. package top.simba1949;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * 输出结果为 a——>b——>c——>a——>b——>c——>...
  6. * 输出内容 等待标记 下一个标记
  7. * a 1 2
  8. * b 2 3
  9. * c 3 1
  10. * @author Anthony
  11. * @date 2020/10/24 10:01
  12. */
  13. public class Application9 {
  14. public static void main(String[] args) throws InterruptedException {
  15. WaitNotify9 waitNotify = new WaitNotify9(10);
  16. Condition conditionA = waitNotify.newCondition();
  17. Condition conditionB = waitNotify.newCondition();
  18. Condition conditionC = waitNotify.newCondition();
  19. new Thread(() -> waitNotify.print("a", conditionA, conditionB), "a").start();
  20. new Thread(() -> waitNotify.print("b", conditionB, conditionC), "b").start();
  21. new Thread(() -> waitNotify.print("c", conditionC, conditionA), "c").start();
  22. // 主要 await 和 signal 方法必须在lock和unlock之间使用
  23. waitNotify.lock();
  24. try{
  25. // 主线程等待数秒之后,保证线程a已经启动,并且进入休息室
  26. Thread.sleep(1000);
  27. // 主线程程唤醒线程 a
  28. conditionA.signal();
  29. }finally {
  30. waitNotify.unlock();
  31. }
  32. }
  33. }
  34. class WaitNotify9 extends ReentrantLock {
  35. private static final long serialVersionUID = 6794432110745744142L;
  36. // 循环次数
  37. private int loopNumber;
  38. public WaitNotify9(int loopNumber) {
  39. this.loopNumber = loopNumber;
  40. }
  41. /**
  42. * 打印
  43. * 设计思路:
  44. * 首先所有线程运行时首先进入休息室condition中,控制线程唤醒顺序,通过主线程唤醒第一个线程开始即可
  45. * @param str
  46. * @param current
  47. * @param next
  48. */
  49. public void print(String str, Condition current, Condition next){
  50. for (int i = 0; i < loopNumber; i++) {
  51. this.lock();
  52. try {
  53. // 线程执行的时候先进入各自的休息室休息
  54. current.await();
  55. // 打印内容,设置等待标记flag
  56. System.out.print(str + "->");
  57. next.signal();
  58. } catch (InterruptedException e) {
  59. e.printStackTrace();
  60. } finally {
  61. this.unlock();
  62. }
  63. }
  64. }
  65. }

park&unpark实现——控制唤醒顺序

  1. package top.simba1949;
  2. import java.util.concurrent.locks.LockSupport;
  3. /**
  4. * 输出结果为 a——>b——>c——>a——>b——>c——>...
  5. * 输出内容 等待标记 下一个标记
  6. * a 1 2
  7. * b 2 3
  8. * c 3 1
  9. * @author Anthony
  10. * @date 2020/10/24 10:01
  11. */
  12. public class Application8 {
  13. static Thread ta;
  14. static Thread tb;
  15. static Thread tc;
  16. public static void main(String[] args) {
  17. WaitNotify8 waitNotify8 = new WaitNotify8(10);
  18. ta = new Thread(() -> waitNotify8.print("a", tb), "ta");
  19. tb = new Thread(() -> waitNotify8.print("b", tc), "tb");
  20. tc = new Thread(() -> waitNotify8.print("c", ta), "tc");
  21. ta.start();
  22. tb.start();
  23. tc.start();
  24. // 主线程唤醒第一个线程
  25. LockSupport.unpark(ta);
  26. }
  27. }
  28. class WaitNotify8 {
  29. // 循环次数
  30. private int loopNumber;
  31. public WaitNotify8(int loopNumber) {
  32. this.loopNumber = loopNumber;
  33. }
  34. /**
  35. * 打印
  36. * 设计思路:
  37. *
  38. */
  39. public void print(String str, Thread next){
  40. for (int i = 0; i < loopNumber; i++) {
  41. // 针对当前线程
  42. LockSupport.park();
  43. System.out.print(str + "->");
  44. LockSupport.unpark(next);
  45. }
  46. }
  47. }