开发者

线程阻塞唤醒工具 LockSupport使用详解

开发者 https://www.devze.com 2023-01-27 10:20 出处:网络 作者: 暮色妖娆丶
目录LockSupport 简介回顾 synchronized 和 LockLockSupport 和 synchronized http://www.devze.com和 Lock 的阻塞方式对比LockSupport 的使用LockSupport 注意事项许可证提前发放许可证不会累计LockSupport 底层实现
目录
  • LockSupport 简介
  • 回顾 synchronized 和 Lock
  • LockSupport 和 synchronized http://www.devze.com和 Lock 的阻塞方式对比
  • LockSupport 的使用
  • LockSupport 注意事项
    • 许可证提前发放
    • 许可证不会累计
  • LockSupport 底层实现
    • 结语

      LockSupport 简介

      LockSupport 是 Java 并发编程中一个非常重要的组件,我们熟知的并发组件 Lock、线程池、CountDownLatch 等都是基于 AQS 实现的,而 AQS 内部控制线程阻塞和唤醒又是通过 LockSupport 来实现的。

      从该类的注释上也可以发现,它是一个控制线程阻塞和唤醒的工具,与以往的不同是它解决了曾经 wait()、notify()、await()、signal() 的局限。

      回顾 synchronized 和 Lock

      我们知道 Java 中实现并发安全通常会通过这两种加锁的方式,对于 synchronized 加锁的方式,如果我们想要控制线程的阻塞和唤醒是通过锁对javascript象的 wait()notify() 方法,以下面循环交替打印 AB 为例

      int status = 2;
      public static void main(String[] args) throws InterruptedException {
          TestSync obj = new TestSync();
           new Thread(() -> {
              synchronized (obj){
           python       while (true){
                      if(obj.status == 1){
                          obj.wait();
                      }
                      System.out.println("A");
                      obj.status = 1;
                      TimeUnit.SECONDS.sleep(1);
                      obj.notify();
                  }
              }
           }).start();
          new Thread(() -> {
             synchronized (obj){
                while (true){
                    if(obj.status == 2){
                        obj.wait();
                    }
                    System.out.println("B");
                    obj.status = 2;
                    TimeUnit.SECONDS.sleep(1);
                    obj.notify();
                }
             }
          }).start();
      }
      

      如果我们使用 Lock 实现类,上述代码几乎是一样的,只是先获取 Condition 对象

       Condition condition = lock.newCondition();
      

      obj.wait() 换成 condition.await()obj.notify() 换成 condition.signal() 即可。

      LockSupport 和 synchronized 和 Lock 的阻塞方式对比

      技术阻塞唤醒方式局限
      synchronized使用锁对象的 wait()、notify()1. 只能用在 synchronized 包裹的同步代码块中 2. 必须先 wait() 才能 notify()
      Lock使用 condition 的 await()、sig开发者_C教程nal()1. 只能用在 lock 锁住的代码块中 2. 必须先 await() 才能 signal()
      LockSupportpark()、unpark(Thread t)没有限制

      LockSupport 的使用

      下面代码中,我们使用 LockSupport 去阻塞和唤醒线程,我们可以多次尝试,LockSupportpark()unpark() 方法没有先后顺序的限制,也不需要捕获异常,也没有限制要在什么代码块中才能使用。

          public static void main(String[] args) throws InterruptedException {
              Thread t1 = new Thread(() -> {
                  System.out.println("A");
                  LockSupport.park();
                  System.out.println("被唤醒");
              });
              t1.start();
              TimeUnit.SECONDS.sleep(2);
              new Thread(() -> {
                  System.out.println("B");
                  LockSupport.unpark(t1);
              }).start();
          }
      

      LockSupport 注意事项

      许可证提前发放

      从该类的注释中我们可以看到这个类存储了使用它的线程的一个许可证,当调用 park() 方法的时候会判断当前线程的许可证是否存在,如果存在将直接放行,否则就阻塞。

      public static void main(String[] args) throws InterruptedException {
          Thread t1 = new Thread(() -> {
              try {
                  TimeUnit.SECONDS.sleep(3);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              System.out.println("A");
              LockSupport.park();//不会阻塞
              System.out.println("被唤醒");
          });
          t1.start();
          TimeUnit.SECONDS.sleep(2);
          new Thread(() -> {
              System.out.println("B");
              System.out.println("先调用 unpark()");
              LockSupport.unpark(t1);
          },"t2").start();
      }
      

      看这www.devze.com个代码示例,这里我们在 t2 中先让线程 t1 unpark(), 然后在 t1 中调用 park(), 结果并不会阻塞 t1 线程。因为在 t2 中调用 LockSupport.unpark(t1); 的时候相当于给 t1 提前准备好了许可证。

      许可证不会累计

      LockSupport.unpark(t1); 无论调用多少次,t1 的通行证只有一个,当在 t1 中调用两次 park() 方法时线程依然会被阻塞。

      public static void main(String[] args) throws InterruptedException {
          Thread t1 = new Thread(() -> {
              try {
                  TimeUnit.SECONDS.sleep(3);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              System.out.println("A");
              LockSupport.park();
              LockSupport.park();
              System.out.println("被唤醒");
          });
          t1.start();
          TimeUnit.SECONDS.sleep(2);
          new Thread(() -> {
       php       System.out.println("B");
              System.out.println("先调用 unpark()");
              LockSupport.unpark(t1);
              LockSupport.unpark(t1);
              LockSupport.unpark(t1);
              LockSupport.unpark(t1);
              LockSupport.unpark(t1);
          },"t2").start();
      }
      

      以上述代码为例,t1 将被阻塞。

      LockSupport 底层实现

      观察源码发现 park() 和 unpark() 最底下调用的是 native() 方法,源码在 C++ 中实现

      @IntrinsicCandidate
      public native void park(boolean isAbsolute, long time);
      @IntrinsicCandidate
      public native void unpark(Object thread);
      

      对,这只是个标题,卷不动了,不去看 C/C++ 了。。。。

      结语

      LockSupport 是 Java 并发编程中非常重要的组件,这是我们下一步阅读 AQS(AbstractQueuedSynchronizer) 源码的基础。总之我们只要记住它是控制线程阻塞和唤醒的工具,并且知道它与其他阻塞唤醒方式的区别即可。

      以上就是线程阻塞唤醒工具 LockSupport使用详解的详细内容,更多关于唤醒 LockSupport线程阻塞的资料请关注我们其它相关文章!

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号