教你如何使用Java多线程编程LockSupport工具类

作者:Tony-devj 时间:2022-03-10 03:21:56 

LockSupport类

用于创建锁和其他同步类的基本线程阻塞原语,此类与使用它的每个线程关联一个许可。如果获得许可,将立即返回对park的调用,并在此过程中消耗掉它;否则may会被阻止。调用unpark可使许可证可用(如果尚不可用)。(不过与信号量不同,许可证不会累积。最多只能有一个。)
方法park和unpark提供了有效的阻塞和解阻塞线程的方法,这些线程不会遇到导致已弃用的方法Thread.suspend和Thread.resume无法用于以下问题:由于许可,在调用park的一个线程与试图进行unpark的另一个线程之间的竞争将保留生命力。此外,如果调用者的线程被中断并且支持超时版本,则park将返回。 park方法也可能在其他任何时间出于“无理由”返回,因此通常必须在循环中调用该循环,该循环在返回时会重新检查条件。从这个意义上说,park是对“繁忙等待”的优化,它不会浪费太多的时间,而必须与unpark配对才能生效。
了解了其作用,我们看看相关API。

LockSupport.park()

park():除非有许可,否则出于线程调度目的禁用当前线程。
如果许可证可用,则将其消耗掉,并立即返回呼叫;否则,将立即返回该呼叫。出于线程调度的目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
1.其他一些线程以当前线程为目标调用unpark()方法
2.其他一些线程Threadinterrupt interrupts当前线程
3.虚假地调用返回
源码:


public static void park() {
   UNSAFE.park(false, 0L);
}

LockSupport都是调用的UNSAFE的方法,我们先看看park方法


public class LockSupportMain {
   public static void main(String[] args) {
       System.out.println("begin park!");
       LockSupport.park();
       System.out.println("end park!");
   }
}

运行结果:

教你如何使用Java多线程编程LockSupport工具类

我们调用了park方法,导致Main线程一直被阻塞,一直没有结束,因为默认的情况下,调用线程是不持有许可证的,我们如何解决呢?上面提到过三个方式,我们一个一个的验证。

1.调用unpack方法获得许可

unpack():如果给定线程尚不可用,则使它可用。如果线程在park上被阻止,则它将取消阻止。否则,将确保其对park的下一次调用不会阻塞。如果给定线程尚未启动,则不能保证此操作完全无效。
源码:


public static void unpark(Thread thread) {
   if (thread != null)
       UNSAFE.unpark(thread);
}

public class LockSupportMain2 {
   public static void main(String[] args) {
       System.out.println("begin park!");
       LockSupport.unpark(Thread.currentThread());
       LockSupport.park();
       System.out.println("end park!");
   }
}

运行结果:

教你如何使用Java多线程编程LockSupport工具类

可以看出,当前的线程main已经释放了,没有进行阻塞,直接运行完成了。
我们创建一个线程有Main线程进行unpark方法将线程在阻塞的情况下,进行运行。


public class LockSupportMain3 {

public static void main(String[] args) throws InterruptedException {
       Runnable runnable = new Runnable() {
           @Override
           public void run() {
               System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
               LockSupport.park();
               System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
           }
       };

Thread thread = new Thread(runnable);
       thread.start();
       Thread.sleep(2000);
       System.out.println("main thread call unpark");
       LockSupport.unpark(thread);
   }

}

运行结果:

教你如何使用Java多线程编程LockSupport工具类

运行结果可以看出,其他一些线程以当前线程为目标调用unpark()方法可以将线程的park导致阻塞的继续运行。

2.调用中断interrupts方法获得许可

由于park方法不会告诉你何种原因返回,所以调用者需要根据之前调用park方法的原因,再次检查条件是否满足,如果不能满足,就还需要调用park方法


public class LockSupportMain4 {

public static void main(String[] args) throws InterruptedException {
       Runnable runnable = new Runnable() {
           @Override
           public void run() {
               System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
               while (!Thread.currentThread().isInterrupted()) {
                   LockSupport.park();
               }
               System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
           }
       };

Thread thread = new Thread(runnable);
       thread.start();
       Thread.sleep(2000);
       //通过interrupt方法 让park阻塞继续运行
       thread.interrupt();
   }

}

运行结果:

教你如何使用Java多线程编程LockSupport工具类

由运行可以看出,其他一些线程Threadinterrupt interrupts当前线程是可以将park阻塞的线程继续运行。

parkNanos(long nanos)

parkNanos(long nanos):除非允许使用许可,否则在指定的等待时间内禁用用于线程调度的当前线程。
源码:


public static void parkNanos(long nanos) {
   if (nanos > 0)
       UNSAFE.park(false, nanos);
}

public class LockSupportMain5 {

public static void main(String[] args) throws InterruptedException {
       Runnable runnable = new Runnable() {
           @Override
           public void run() {
               System.out.println("begin start thread name: " + Thread.currentThread().getName() + " park");
               LockSupport.parkNanos(3_000_000_000L);
               System.out.println("end start thread name: " + Thread.currentThread().getName() + " park");
           }
       };
       Thread thread = new Thread(runnable);
       thread.start();
   }
}

运行结果:

教你如何使用Java多线程编程LockSupport工具类

三秒之后,阻塞三秒之后继续运行。

park(Object blocker)

park(Object blocker):除非有许可,否则出于线程调度目的禁用当前线程
源码:


public static void park(Object blocker) {
   Thread t = Thread.currentThread();
   setBlocker(t, blocker);
   UNSAFE.park(false, 0L);
   setBlocker(t, null);
}

这里的blocker对象是Thread类中的blocker对象,代码如下:


//提供给java.util.concurrent.locks.LockSupport.park的当前调用的参数。
//由(私有)java.util.concurrent.locks.LockSupport.setBlocker设置使用
//java.util.concurrent.locks.LockSupport.getBlocker进行访问
volatile Object parkBlocker;

parkNanos(Object blocker, long nanos)

源码:


public static void parkNanos(Object blocker, long nanos) {
   if (nanos > 0) {
       Thread t = Thread.currentThread();
       setBlocker(t, blocker);
       UNSAFE.park(false, nanos);
       setBlocker(t, null);
   }
}

parkUntil(Object blocker, long deadline)

parkUntil(Object blocker, long deadline):除非指定许可,否则禁用当前线程以进行线程调度,直到指定的期限。
源码:


public static void parkUntil(Object blocker, long deadline) {
   Thread t = Thread.currentThread();
   setBlocker(t, blocker);
   UNSAFE.park(true, deadline);
   setBlocker(t, null);
}

我们使用java API中的例子看看:


public class LockSupportMain6 {
   private final AtomicBoolean locked = new AtomicBoolean(false);
   private final Queue<Thread> waiters
           = new ConcurrentLinkedQueue<Thread>();

public void lock() {
       boolean wasInterrupted = false;
       Thread current = Thread.currentThread();
       waiters.add(current);

// 不在队列中时先阻塞或无法获取锁定
       while (waiters.peek() != current ||
               !locked.compareAndSet(false, true)) {
           LockSupport.park(this);
           // 等待时忽略中断 如果park方法是因为被中断而返回,则忽略中断,并且重新设置中断标记,做个标记
           if (Thread.interrupted()) {
               wasInterrupted = true;
           }
       }
       waiters.remove();
       // 退出时重新声明中断状态
       if (wasInterrupted)          
       {
           current.interrupt();
       }
   }
   public void unlock() {
       locked.set(false);
       LockSupport.unpark(waiters.peek());
   }
}

有blocker的可以传递给开发人员更多的现场信息,可以查看到当前线程的阻塞对象,方便定位问题

来源:https://blog.csdn.net/qian1314520hu/article/details/115667930

标签:java,LockSupport,工具类,多线程编程
0
投稿

猜你喜欢

  • Jenkins 关闭和重启详细介绍及实现

    2022-09-19 00:57:12
  • Android之复选框对话框用法实例分析

    2023-10-03 05:07:03
  • Unity创建平铺网格地图的方法

    2022-07-06 16:05:49
  • c#实现万年历示例分享 万年历农历查询

    2022-11-08 23:26:57
  • Android录音功能的实现以及踩坑实战记录

    2022-01-06 14:03:29
  • java实现银行ATM管理系统

    2023-10-30 14:50:51
  • C++ 让函数返回数组的方法

    2022-12-04 06:49:33
  • java中ArrayList和LinkedList的区别详解

    2022-09-05 11:13:48
  • 七个Spring核心模块详解

    2021-07-25 15:32:18
  • Java身份证验证方法实例详解

    2023-04-04 02:22:11
  • SpringBoot详细讲解yaml配置文件的用法

    2022-04-21 16:29:33
  • Android批量插入数据到SQLite数据库的方法

    2022-09-28 06:18:06
  • jar命令的用法详解

    2022-08-14 13:37:28
  • Java基础之容器Vector详解

    2023-11-25 13:10:07
  • Java 动态代理的多种实现方式

    2023-11-20 05:23:14
  • 剑指Offer之Java算法习题精讲数组与字符串

    2021-05-24 19:21:45
  • SpringBoot 开发提速神器 Lombok+MybatisPlus+SwaggerUI

    2022-07-08 07:40:23
  • springboot省去配置Tomcat的步骤问题

    2023-03-13 18:58:44
  • 不使用他人jar包情况下优雅的进行dubbo调用详解

    2022-04-20 11:43:46
  • java图搜索算法之DFS与BFS详解

    2022-08-15 15:48:57
  • asp之家 软件编程 m.aspxhome.com