java并发编程——Java并发包中锁的原理剖析(LockSupport工具类)

LockSupport工具类

        JDK中的rt.jar包里面的LockSupport是一个工具类,它的主要作用是挂起好唤醒线程,该工具类是创建锁和其他同步类的基础。

        LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。LockSupport是使用Unsafe类实现的,下面介绍LockSupport中的几个主要函数。

1、void park()方法

        如果调用park方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.park()时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。

        如下代码直接在main函数里面调用park方法,最终只会输出begin park!,然后当前线程被挂起,这时因为默认情况下调用线程是不持有许可证的。
在这里插入图片描述

        在其他线程调用unpark(Thread thread)方法并且讲当前线程作为参数时,调用park方法而被阻塞的线程会返回。另外,如果其他线程调用了阻塞线程的interrupt()方法,设置了中断标志或者线程被虚假唤醒,则阻塞线程也会返回。所以在调用park方法时最好也是用循环条件判断方式。

        需要注意的是,因调用park()方法而被阻塞的线程被其他线程中断而返回时并不会抛出InterruptedException异常。

2、void unpark(Thread thread)方法

        当一个线程调用unparkt时,如果参数thread线程没有持有thread与LockSupport类关联的许可证,则让thread线程持有。如果thread之前因调用park()而被挂起,则调用unpark后,该线程会被唤醒。如果thread之前没有调用park,则调用unpark方法后,再调用park方法,其会立刻返回。

在这里插入图片描述
在这里插入图片描述

下面再来看一个例子以加深对park和unpark的理解
在这里插入图片描述
输出结果为
在这里插入图片描述

使用interrupt方法也可以唤醒线程
在这里插入图片描述
输出结果为
在这里插入图片描述

3、void parkNanos(long nanos)方法

        和park方法类似,如果调用park()方法的线程已经拿到了与LockSupport关联的许可证,则调用LockSupport.parkNanos(long nanos)方法后会立马返回。该方法的不同在于,如果没有拿到许可证,则调用线程会被挂起nanos时间后修改为自动返回。

        另外park方法还支持带有blocker参数的方法void park(Object blocker)方法,当线程在没有持有许可证的情况下调用park方法而被阻塞挂起时,这个blocker对象会被记录到该线程内部。

        使用诊断工具可以观察线程被阻塞的原因,诊断工具是通过调用getBlocker(Thread thread)方法来获取blocker对象的,所以JDK推荐我们使用带有blocker参数的park方法,并且blocker被设置为this,这样当在打印线程堆栈排查问题时就能知道是哪个类被阻塞了。

在这里插入图片描述
获取线程的pid可以使用jvisualvm命令打开如图
在这里插入图片描述

运行代码后,使用jstack pid命令查看线程堆栈时,可以看到如下输出结果。
在这里插入图片描述
修改代码(1)为LockSupport.park(this)后运行代码,则jstack pid的输出结果为
在这里插入图片描述

4、park(Object blocker)方法

在这里插入图片描述
        Thread类里面有个变量volatile Object parkBlocker,用来存放park方法传递的blocker对象,也就是把blocker变量存放到调用park方法的线程的成员变量里面。

5、void parkNanos(Object blocker, long nanos)方法

        相比park(Object blocker)方法多了一个超时时间。

6、void parkUnitil(Object blocker, long deadline)方法

在这里插入图片描述
        其中参数deadline的时间单位是ms,该时间是从1970年到现在某一个时间点的毫秒值。这个方法和parkNanos(object blocker, long nanos)方法的区别是,后者是从当前算等待nanos秒时间,而前者是指定一个时间点,比如需要等到2017.12.11日12:00:00,则把这个时间点转换为从1970年到这个时间点的总毫秒数。

最后一个例子
在这里插入图片描述
        这是一个先进先出的锁,也就是只有队列的首元素可以获取锁。在代码(1)处,如果当前线程不是队首或者当前锁已经被其他线程获取,则调用park方法挂起自己。

        然后在代码(2)处判断,如果park方法因为被中断而返回,则忽略中断,并且重置中断标志,做个标记,然后再次判断当前线程是不是队首元素或者当前锁是否已经被其他线程获取,如果是则继续调用park方法挂起自己。

        然后在代码(3)中,判断标记,如果标记为true则中断该线程,这个怎么理解呢?其实就是其他线程中断了该线程,虽然我对中断信号不感兴趣,忽略他,但是不代表其他线程对该标志不感兴趣,所以要恢复下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值