LockSupport是什么
- LockSupport是JUC下的一个线程同步工具类,实现了线程的阻塞和唤醒操作。相比其他同步机制,如Synchronized、ReentrantLock等,LockSupport的性能更高、更灵活,同时也可以避免线程操作不当引起的死锁问题。
- Java中Lock和LockSupport的区别是:Lock是一个接口,它定义了锁获取和释放的基本操作;而LockSupport是用来创建锁和其他同步类的基本线程阻塞原语,它定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能。 Lock是java.util.concurrent.locks包下的接口,定义了锁获取和释放的基本操作。
- LockSupport底层通过操作系统的底层原子操作实现。在Linux操作系统中,主要是通过futex系统实现。
Demo演示
package org.example;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class Main {
public static void main(String[] args) {
Thread t = new Thread((Runnable) () -> {
for (int i = 0; i < 10; i++) {
System.out.println(i);
if (i == 5) LockSupport.park();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "Thread-1");
t.start();
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("after 10 seconds");
LockSupport.unpark(t);
}
}
底层实现&使用场景
Thread对象的native实现里有一个成员代表线程的中断状态,我们可以认为它是一个bool型的变量。初始为false。
Thread对象的native实现里有一个成员代表线程是否可以阻塞的许可permit,我们可以认为它是一个int型的变量,但它的值只能为0或1。当为1时,再累加也会维持1。初始为0。
线程默认的permit
是0,中断状态
为false,
park方法
park() {
if(permit > 0) {
permit = 0;
return;
}
if(中断状态 == true) {
return;
}
阻塞当前线程; // 将来会从这里被唤醒
if(permit > 0) {
permit = 0;
}
}
park
调用后一定会消耗掉permit
,无论unpark
操作先做还是后做
在park方法中:
如果permit为1或者中断标记为true,线程不能阻塞,无论怎么样permit都会置为0
unpark方法
unpark(Thread thread) {
if(permit < 1) {
permit = 1;
if(thread处于阻塞状态)
唤醒线程thread;
}
}
unpark
一定会将permit
置为1,如果线程阻塞,再将其唤醒。从实现可见,无论调用几次unpark
,permit
只能为1。
应用场景
- 生产者和消费者模型
- 线程等待
补充
interrupt
interrupt(){
if(中断状态 == false) {
中断状态 = true;
}
unpark(this); //注意这是Thread的成员方法,所以我们可以通过this获得Thread对象
}
interrupt()
会设置中断状态
为true。注意,interrupt()
还会去调用unpark
的,所以也会把permit
置为1的。
sleep
sleep(){
if(中断状态 == true) {
中断状态 = false;
throw new InterruptedException();
}
线程开始等待;
if(中断状态 == true) {
中断状态 = false;
throw new InterruptedException();
}
}
sleep()
会去检测中断状态,如果检测到了,那就消耗掉中断状态后,抛出中断异常。但sleep()
不会去动permit
。
wait/join
效果同sleep