教你如何使用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!");
}
}
运行结果:
我们调用了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!");
}
}
运行结果:
可以看出,当前的线程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);
}
}
运行结果:
运行结果可以看出,其他一些线程以当前线程为目标调用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();
}
}
运行结果:
由运行可以看出,其他一些线程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();
}
}
运行结果:
三秒之后,阻塞三秒之后继续运行。
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


猜你喜欢
- Hutool Java工具类库_ExcelUtil依赖<!--Hutool Java工具包--> &l
- 本文讨论了Spring Data JDBC如何实现DDD中聚合根存储的设计思路,其中主要讨论了是不是每个实体都需要一个对应数据表,这种问题需
- 用了多年的Visual Studio,今天才发现这个编码技巧,真是惭愧,分享出来,算是抛砖引玉吧!开发环境: vs2010+C#1、代码重构
- 一、概述IDEA自带的注释模板一般都很简单,然而我们在写代码的时候喜欢把类注释和文档注释写在代码里,既方便自己看所有的参数,也便于以后维护代
- C# 5.0 给我们带来了三个非常有用的编译器特性CallerMemberNameCallerFilePathCallerLineNumbe
- C语言学习建议1.阅读文章一本更有意义又有趣的书《C专家编程》这本书叙述了各种各样趣味的八卦,例如设计方案一个程序流程来查验过道里的自动售卖
- ConstantConstant 和 ConstantPool 是用于表示常量的一种机制。Constant 接口定义了常量的基本属性和方法,
- C#生成指定范围内的不重复随机数// Number随机数个数// minNum随机数下限// maxNum随机数上限public int[]
- INotifyPropertyChanged:该接口包含一个事件, 针对属性发生变更时, 执行该事件发生。// /
- 前言:最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到
- springboot 配置多个jndi数据源1.在application.properties中,添加jndi配置如下图2.新建dataSo
- 本文实例为大家分享了Flutter实现底部导航栏的具体代码,供大家参考,具体内容如下效果实现先将自动生成的main.dart里面的代码删除,
- 本文实例为大家分享了C++实现希尔排序的具体代码,供大家参考,具体内容如下一、思路:希尔排序:又称缩小增量排序,是一种改进的插入排序算法,是
- 这是一个及其常见的问题,网上已经有关于这个问题的很多讨论。但是我觉得都是不求甚解,有一些还是在误导别人。下面我来说下我对这三者的理解,如有错
- 本文实例为大家分享了java实现马踏棋盘的具体代码,供大家参考,具体内容如下马踏棋盘算法介绍8X8棋盘,马走日字,要求每个方格只进入一次,走
- 介绍Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。Spring Cache提供了一
- Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全
- java中的set接口有如下的特点:不允许出现重复元素;集合中的元素位置无顺序;有且只有一个值为null的元素。因为java中的set接口模
- 简介相机模块库,自定义相机,通过简单的调用即可实现拍照、图片裁剪、录像及录像抓拍功能;实现图片压缩,减少图片体积;自定义相机可避免使用系统相
- 1.概述MybatisPlus是国产的第三方插件, 它封装了许多常用的CURDapi,免去了我们写mapper.xml的重复劳动,这里介绍了