线程同步的一些思考

文章讨论了如何在Java中实现线程间的顺序打印,以避免死锁问题。通过设置标志变量isPrintA和使用synchronized块,确保A和B线程按顺序执行。关键点在于持有锁的线程在执行完其关键操作后先调用notify()唤醒其他等待线程,然后再调用wait()让自己进入等待状态,防止出现死锁。错误的实现可能导致线程A和B同时阻塞,造成死锁。
摘要由CSDN通过智能技术生成

线程同步的一些思考

A、B轮流

这种没有顺序依赖的打印场景,只需要将notify唤醒提前,就不会有死锁问题

public class PrintUtile {

  private static final Object lock = new Object();

  public static void main(String[] args) throws InterruptedException {
    Thread threadA = new Thread(new PrintA());
    Thread threadB = new Thread(new PrintB());

//    extractedA(threadA, threadB);
    extractedB(threadA, threadB);
  }

  private static void extractedA(Thread threadA, Thread threadB) throws InterruptedException {
    threadA.start();
    Thread.sleep(1000);
    threadB.start();
  }
  private static void extractedB(Thread threadA, Thread threadB) throws InterruptedException {
    threadB.start();
    Thread.sleep(1000);
    threadA.start();
  }

  static class PrintA implements Runnable {

    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        synchronized (lock) {
            System.out.println("A");
            lock.notify();
          try {
            lock.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }

  static class PrintB implements Runnable {

    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        synchronized (lock) {
          System.out.println("B");
          lock.notify();
          try {
            lock.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }
}

先A后B

正确写法:

class PrintUtile {

  private static final Object lock = new Object();
  private static boolean isPrintA = false;

  public static void main(String[] args) throws InterruptedException {
    Thread threadA = new Thread(new PrintA());
    Thread threadB = new Thread(new PrintB());

//    extractedA(threadA, threadB);
    extractedB(threadA, threadB);
  }

  private static void extractedA(Thread threadA, Thread threadB) throws InterruptedException {
    threadA.start();
    Thread.sleep(1000);
    threadB.start();
  }
  private static void extractedB(Thread threadA, Thread threadB) throws InterruptedException {
    threadB.start();
    Thread.sleep(1000);
    threadA.start();
  }

  static class PrintA implements Runnable {

    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        synchronized (lock) {
          if (!isPrintA) {
            System.out.println("A");
            isPrintA = true;
            lock.notify();
          }
          try {
            lock.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
  }

  static class PrintB implements Runnable {

    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        synchronized (lock) {
          if (!isPrintA) {
            try {
              lock.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          System.out.println("B");
          isPrintA = false;
          lock.notify();
        }
      }
    }
  }
}

总结:
说一下这种场景的特点,就是有顺序,那么意味着需要标识。意味着其它n个线程执行只有其中一个满足条件的线程能够往下执行然后通知,这个很重要,因为线程抢夺资源我们通常不会进行控制,也就是意味着线程刚开始的执行顺序我们是无法控制的。那么也就意味着在其他n-1条线程先执行的情况下,其他线程肯定都会阻塞,也就是说会wait()。那么这里就有一个编码干货,就是目标第一个执行的线程必须先通知然后再阻塞,也就是先notify()再wait()。

误区:之前写的时候会出现这么一种错误,造成了死锁。
写的思路是这样的

  1. 线程A先进行打印,然后wait,在notify
  2. 线程B在打印,然后notify
    这种思路在线程A先拿到CPU资源的情况下没有问题,但是如果是线程B先拿到CPU资源的话就会导致线程B先wait阻塞了,然后线程A执行打印了,但是先执行了wait,导致A、B同时阻塞,没有其它线程进行通知,造成死锁的情况。

思考,就是A肯定是第一个要执行关键代码逻辑的线程,而其它线程可能都会先行阻塞,所以既然A是第一个会执行关键逻辑代码的线程,那么执行完毕后A虽然也需要阻塞waite,但是在阻塞之前要先通知notify及时的唤醒其它线程才行。

  static class PrintA implements Runnable {

    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        synchronized (lock) {
          if (!isPrintA) {
            System.out.println("A");
            isPrintA = true;
            try {
              lock.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          lock.notify();
        }
      }
    }
  }

  static class PrintB implements Runnable {

    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        synchronized (lock) {
          if (!isPrintA) {
            try {
              lock.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          System.out.println("B");
          isPrintA = false;
          lock.notify();
        }
      }
    }
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值