一、LockSupport工具类简介
LockSupport类是一个工具类,它位于JDK中的rt.jar包里面,它的主要作用是对线程进行挂起和唤醒,它是创建锁和其他同步类的基础。
LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。
LockSupport类是使用Unsafe类实现的。
二、阅读LockSupport类的源码
LockSupport类中含义几个主要的方法,具体如下:
1. void park()方法:
如果调用LockSupport.park()方法的线程已经拿到了与LockSupport关联的许可证,则调用时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。
pack()函数的源码如下:
public static void park() {
//挂起线程
UNSAFE.park(false, 0L);
}在其他线程调用LockSupport.unpark(Thread thread)方法并且将当前线程作为参数时,调用LockSupport.park()方法而被阻塞的线程会返回。
另外,如果其他线程调用了阻塞线程的interrupt()方法(中断线程),设置了中断标志或者线程被虚假唤醒,则阻塞线程也会返回。所以在调用LockSupport.park()方法时最好使用循环条件判断。
注意:因为调用LockSupport.park()方法而被阻塞的线程被其他线程中断而返回时,并不会抛出InterruptedException异常。
park()方法返回时不会告诉你因何种原因返回,所以调用者需要根据之前调用park方法的原因,再次检查条件是否满足,如果不满足则还需要再次调用park()方法
示例代码:
import java.util.concurrent.locks.LockSupport;
/**
* @ClassName: LockSupportDemo
* @Description: LockSupport工具类: 它的主要作用是对线程进行挂起和唤醒,它是创建锁和其他同步类的基础。
* 如果调用LockSupport.park()方法的线程已经拿到了与LockSupport关联的许可证,则调用时会马上返回,
* 否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。
* @Author: liuhefei
* @blog: http://www.xianlaiwan.cn/u/1323320/articles
**/
public class LockSupportDemo {
public static void main(String[] args) {
System.out.println("start...");
//直接在main函数中调用park()方法最终只会输出start...,然后当前线程被挂起。
//在默认情况下,调用线程是不持有许可证的。
LockSupport.park();
System.out.println("end...");
}
}pack(Object blocker)方法的源码如下:
public static void park(Object blocker) {
//获取调用线程
Thread t = Thread.currentThread();
//设置该线程的blocker变量
setBlocker(t, blocker);
//挂起线程
UNSAFE.park(false, 0L);
//线程被激活后清除blocker变量,因为一般都是在线程阻塞时才会分析原因
setBlocker(t, null);
}Thread类中有个变量volatile Object parkBlocker,用来存放park方法传递的blocker对象,也就是把blocker变量存放到了调用park方法的线程的成员变量里面。
当线程在没有持有许可证的情况下,调用LockSupport.park(blocker)方法而被阻塞挂起时,这个blocker对象会被记录到该线程的内部。
使用线程诊断工具可以观察线程被阻塞的原因,诊断工具是通过调用getBlocker(Thread)方法来获取blocker对象的,所以JDK推荐我们使用带有blocker参数的park方法,并且blocker被设置为this,这样当打印线程堆栈排插问题时就能知道是哪个类被阻塞了。
2. void unpark(Thread thread)方法:
当一个线程调用LockSupport.unpark()方法时,如果参数thread线程没有持有thread与LockSupport类关联的许可证,则让thread线程持有;
而如果thread之前因调用LockSupport.park()方法时被挂起,则调用LockSupport.unpark()方法后,该线程会被唤醒。
而如果thread之前没有调用LockSupport.park()方法,则调用LockSupport.unpark()方法后,再调用LockSupport.park()方法,其会立刻返回。
unpark()方法的源码如下:
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}示例代码1:
import java.util.concurrent.locks.LockSupport;
/**
* @ClassName: LockSupportDemo1
* @Description: LockSupport工具类
* @Author: liuhefei
* @blog: http://www.xianlaiwan.cn/u/1323320/articles
**/
public class LockSupportDemo1 {
public static void main(String[] args) {
System.out.println("begin park!");
//使用当前线程获取到许可证
LockSupport.unpark(Thread.currentThread());
//再次调用LockSupport.park()方法
LockSupport.park();
System.out.println("end park!"); //立马返回
}
}示例代码2:
import java.util.concurrent.locks.LockSupport;
/**
* @ClassName: LockSupportDemo2
* @Description: LockSupport工具类
* @Author: liuhefei
* @blog: http://www.xianlaiwan.cn/u/1323320/articles
**/
public class LockSupportDemo2 {
public static void main(String[] args) throws InterruptedException{
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程开始...");
//调用LockSupport.park()方法,挂起自己
//默认情况下,子线程没有持有许可证,因而它会把自己挂起
LockSupport.park();
System.out.println("子线程结束...");
}
});
//启动子线程
threadOne.start();
//主线程休眠2秒
Thread.sleep(2000);
System.out.println("主线程运行...");
//调用LockSupport.unpark()方法让threadOne线程持有许可证,然后LockSupport.park()方法返回
LockSupport.unpark(threadOne);
//park()方法返回时不会告诉你因何种原因返回,所以调用者需要根据之前调用park方法的原因,再次检查条件是否满足,如果不满足则还需要再次调用park()方法
}
}3. void parkNanos(long nanos)方法:
该方法的作用与park方法相似,如果调用LockSupport.park()方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.parkNanos(long nanos)方法后会马上返回。
该方法与park()方法的不同之处在于,如果没有拿到许可证,则调用线程会被挂起nanos时间后修改为自动返回。
parkNanos(long nanos)方法的源码如下:
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}4. void parkNanos(Object blocker, long nanos)方法:
与park(Object blocker)方法相比,只是多了一个超时时间,其源码如下:
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
// 获取调用线程
Thread t = Thread.currentThread();
//设置该线程的blocker变量
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}5. void parkUntil(Object blocker, long deadline)方法,其源码如下:
public static void parkUntil(Object blocker, long deadline) {
//获取调用线程
Thread t = Thread.currentThread();
//设置该线程的blocker变量
setBlocker(t, blocker);
UNSAFE.park(true, deadline); //isAbsolute=true,time=deadline 表示到deadline时间后返回
setBlocker(t, null);
}其中参数deadline的时间单位为ms,该时间是从1970年到现在某一个时间点的毫秒值。
该方法与void parkNanos(Object blocker, long nanos)方法的区别是:
void parkNanos(Object blocker, long nanos)方法是从当前计算等待nanos秒时间;
void parkUntil(Object blocker, long deadline)方法是指定一个时间点,比如需要等到2019.12.22日12:00:00,则把这个时间点转换为从1970年到这个时间的总毫秒数。
示例代码:
import java.util.concurrent.locks.LockSupport;
/**
* @ClassName: LockSupportDemo3
* @Description: LockSupport工具类
* 调用park()方法后的线程被中断后会返回
* 只有中断子线程,子线程才会运行结束,如果子线程不被中断,即使你调用unpark(thread)方法子线程也不会结束
* @Author: liuhefei
* @blog: http://www.xianlaiwan.cn/u/1323320/articles
**/
public class LockSupportDemo3 {
public static void main(String[] args) throws InterruptedException{
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程开始执行...");
//默认情况下,子线程不持有许可证, 调用park方法挂起自己,只有被中断才会退出循环
while (!Thread.currentThread().isInterrupted()){
LockSupport.park();
}
System.out.println("子线程结束...");
}
});
//启动子线程
threadOne.start();
//主线程休眠2秒
Thread.sleep(2000);
System.out.println("主线程执行....");
//中断子线程
threadOne.interrupt();
}
}参考:《Java并发编程之美》
请诸君多多支持!
共同學習,寫下你的評論
評論加載中...
作者其他優質文章
