一、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: https://www.imooc.com/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: https://www.imooc.com/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: https://www.imooc.com/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: https://www.imooc.com/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并发编程之美》
请诸君多多支持!