线程阻塞唤醒工具 LockSupport使用详解
作者:暮色妖娆丶 发布时间:2023-11-29 17:16:10
LockSupport 简介
LockSupport
是 Java 并发编程中一个非常重要的组件,我们熟知的并发组件 Lock、线程池、CountDownLatch
等都是基于 AQS
实现的,而 AQS
内部控制线程阻塞和唤醒又是通过 LockSupport
来实现的。
从该类的注释上也可以发现,它是一个控制线程阻塞和唤醒的工具,与以往的不同是它解决了曾经 wait()、notify()、await()、signal()
的局限。
回顾 synchronized 和 Lock
我们知道 Java 中实现并发安全通常会通过这两种加锁的方式,对于 synchronized
加锁的方式,如果我们想要控制线程的阻塞和唤醒是通过锁对象的 wait()
和 notify()
方法,以下面循环交替打印 AB 为例
int status = 2;
public static void main(String[] args) throws InterruptedException {
TestSync obj = new TestSync();
new Thread(() -> {
synchronized (obj){
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()、signal() | 1. 只能用在 lock 锁住的代码块中 2. 必须先 await() 才能 signal() |
LockSupport | park()、unpark(Thread t) | 没有限制 |
LockSupport 的使用
下面代码中,我们使用 LockSupport
去阻塞和唤醒线程,我们可以多次尝试,LockSupport
的 park()
和 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();
}
看这个代码示例,这里我们在 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(() -> {
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++
了。。。。
来源:https://juejin.cn/post/7192019058399641658
猜你喜欢
- 我object != null要避免很多NullPointerException。有什么替代方法:if (someobject != nul
- 面试官:sychronized关键字有哪些特性?应聘者:可以用来修饰方法;可以用来修饰代码块;可以用来修饰静态方法;可以保证线程安全;支持锁
- 微服务治理Spring Cloud 工具套件为微服务治理提供了全面的技术支持。这些治理工具主要包括服务的注册与发现、负载均衡管理、动态路由、
- 本文实例讲述了Android中TextView显示插入的图片实现方法。分享给大家供大家参考,具体如下:Android系统默认给TextVie
- 首先:因为工作需要,需要对接socket.io框架对接,所以目前只能使用netty-socketio。websocket是不支持对接sock
- 笔者语录: 我发现我喜欢捣鼓一些小玩意儿,虽然官网(见文末)写得很明白了,但是咱们对感兴趣的部分来敲一遍代码好吧。过滤器简介:简介logba
- 废话开篇:iOS与android在实现列表界面的时候是有重用机制的,目的就是减少内存开销,用时间换空间。个人感觉flutter并没有特别强调
- 前言本文章主要从spring security安全认证登录内部调用流程来流程分析登录过程。一、登录时序图时序原图二、配置与代码1.引入库po
- 一、前言知识补充:Arrays.copyOf函数:public static int[] copyOf(int[] original, in
- 条件:1、android:ellipsize=”marquee”2、TextView必须单行显示,即内容必须超出TextView
- 近日于LeetCode看题遇1114 按序打印,获悉一解法使用了Semaphore,顺势研究,记心得于此。此解视Semaphore为锁,以保
- Android Studio 打包 jar 及 aar 包创建工程 New -> Module -> Library在gradl
- 1. 异常1.1 try…catch异常处理try catch的异常处理的格式写法 :try{ &nbs
- 今天突发奇想,想做一个智能拼图游戏来给哄女友。需要实现这些功能第一图片自定义第二宫格自定义,当然我一开始就想的是3*3 4*4 5*5,没有
- 前言对于 InterruptedException,一种常见的处理方式是 “生吞(swallow)” 它 —— 捕捉它,然后什么也不做(或者
- 传统的多分支方式(圈复杂度为6):public String order(String type) { if ("1&
- 建造者模式概述建造者模式(Builder Pattern)属于创建型模式。它是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同
- 1、前言当提及如何终止一个线程时,部分读者通常立马想到的方法肯定是stop(),但是stop()方法并不被推荐使用(很多规范中是禁止使用的)
- 本文实例讲述了Java编程实现获取当前代码行行号的方法。分享给大家供大家参考,具体如下:最近的项目中,为了实现自定义的log类,能够输出具体
- List list=new ArrayList()是怎么回事首先明确List是接口,ArrayList是它的实现类以下两种方法都可以,但是不