理解Java高级并发工具类LockSupport

LockSupport是并发工具包中的一个辅助类,在直接的开发过程中很少直接使用,但是它的身影已经遍布各种工具类,众所周知,AbstractQueuedSynchronizer简称(AQS)是Java并发包的基石之一,很多很实用的工具类,如下:

ReentrantLock
ReadLock
WriteLock
Semaphore
CountDownLatch

都是采用AbstractQueuedSynchronizer辅助构建的,而在AbstractQueuedSynchronizer类中却大量采用了LockSupport来操作各种锁的状态,所以我们很有必要来了解学习这个类。

LockSupport的原理是,它最多只允许有一个许可证令牌(permit),如果线程可以得到这个令牌,那么他就能继续运行下去,否则将会处于阻塞状态,注意此时对应的线程状态是WAITTING不是BLOCKED这个后面我们在细说,先来看下这个类的几个方法:

park()
park(Object blocker)
parkNanos(long nanos)
parkNanos(Object blocker, long nanos)
parkUntil(long deadline)
parkUntil(Object blocker, long deadline)

unpark(Thread thread)

getBlocker(Thread t)

(一)park系列的方法

其中park前缀开头的方法,是用来获取许可证的,如果当前线程的许可证令牌存在1个,那么调用park相关方法的线程会立马返回继续执行,同时许可证个数就变成0个,如果再继续调用park方法,那么当前线程就会阻塞处于等待状态(WAITTING),除非有以下几种情况,才会被唤醒:

(1)任何其他的线程对该线程对象调用了unpark方法,归还了令牌,那么就会解除等待。这里需要注意令牌最多只会有一个,多次调用unpark方法令牌个数是不会随之累加的。

(2)任何其他的线程,对该线程调用了interrupt方法,打断了线程

(3)由于硬件操作系统不可预知的原因,导致了虚假唤醒

(4)调用了下面的一系列方法,可以在时间过期后自动唤醒。

parkNanos(long nanos)
parkNanos(Object blocker, long nanos)
parkUntil(long deadline)
parkUntil(Object blocker, long deadline)

所以park方法,常用的编程模板是:

while (!canProceed()) {
  LockSupport.park(this); 
 }

这里面while循环的作用,就是为了避免虚假唤醒的情况,此外在配合使用自旋锁的时候,park方法可以使得当前线程进入等待状态,从而可以避免大量自旋导致的cpu资源的浪费。

(二)unpark方法

调用这个方法,可以保证归还一个许可证令牌,从而保证下次调用park方法的线程可以直接立即返回运行,这里需要注意的是对于一个没有启动的线程,这里不保证有任何影响。

(三)关于park和getBlocker方法的对象参数的作用

park(Object blocker)
getBlocker(Thread t)

park方法的blocker参数的作用是可以传入一个对象,记录一些监控信息,这样当线程处于阻塞时候,我们可以调用getBlocker方法读取这个对象,从而获取这个阻塞线程的一些情况,注意这里获取的仅仅是最后一次park的对象信息,因为park方法可以调用无数次,所以这里只能保证获取最新的自定义的对象监控信息。

(四)使用例子

关于park和unpark一般都是成对出现的,下面看一个oracle官网给的使用LockSupport做的一个公平队列锁的代码:

private final AtomicBoolean locked = new AtomicBoolean(false);
    private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();

    public void lock() {
        boolean wasInterrupted = false;
        Thread current = Thread.currentThread();
        //先把自己加到队列里面
        waiters.add(current);

        // Block while not first in queue or cannot acquire lock
        //peek取出头元素但不移除,判断头元素是否等当前线程 ,如果不是当前元素,直接进入循环
        //如果头不是当前线程,那么判断CAS指令,如果成功直接执行代码,否则进入循环阻塞
        while (waiters.peek() != current || !locked.compareAndSet(false, true)) {
            LockSupport.park(this);
            if (Thread.interrupted()) // ignore interrupts while waiting
                wasInterrupted = true;
        }

        waiters.remove();
        if (wasInterrupted)          // reassert interrupt status on exit
            current.interrupt();
    }

    public void unlock() {
        locked.set(false);
        LockSupport.unpark(waiters.peek());
    }

(五)相关问题

(1)park和unpark方法在官网说明书里面并没有提到它们有happends-before关系,所以这里不能依赖它们保证相关的原子性,可见性和有序性。

(2)park调用会导致当前的线程处于WAITTING或者WAITTING_TIMED状态,当调用unpark方法时并不会立即进入RUNNABLE状态,而是看当前有没有线程已经占用锁,如果有它们会先变成BLOCKED状态,然后在变成RUNNABLE状态

(3)WAITTING与BLOCKED状态的区别,简单的理解WAITTING一般需要有人通知才能唤醒,而BLOCKED通常是只要对象的锁没人占用就可以继续运行,如果有人占用则进入BLOCKED阻塞

(4)LockSupport类一般不会直接出现在代码里,绝大多数时候我们只需要使用AbstractQueuedSynchronizer这个封装好的工具框架类即可。

总结:

本文详细的介绍了LockSupport工具类的方法api和功能说明,并且分析了该类的相关注意事项和问题,由于该类是AQS框架的一个重要组成部分,所以我们很有必要去清晰的掌握和了解它。

原文发布于微信公众号 - 我是攻城师(woshigcs)

原文发表时间:2018-08-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏积累沉淀

Java批处理

批处理 JDBC对批处理的操作,首先简单说一下JDBC操作sql语句的简单机制。 JDBC执行数据库操作语句,首先需要将sql语句打包成为网络字...

4715
来自专栏积累沉淀

Java面试笔试题大汇总(最全+详细答案)

声明:有人说, 有些面试题很变态,个人认为其实是因为我们基础不扎实或者没有深入。本篇文章来自一位很资深的前辈对于最近java面试题目所做的总结归纳,有170道题...

7.7K11
来自专栏屈定‘s Blog

Java--CAS操作分析

CAS操作在Java中的应用很广泛,比如ConcurrentHashMap,ReentrantLock等,其常被用来解决独占锁对线程阻塞而导致的性能低下问题,是...

6283
来自专栏菩提树下的杨过

ZooKeeper 笔记(6) 分布式锁

  目前分布式锁,比较成熟、主流的方案有基于redis及基于zookeeper的二种方案。   大体来讲,基于redis的分布式锁核心指令为SETNX,即如果目...

2458
来自专栏coolblog.xyz技术专栏

Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析

在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concu...

86513
来自专栏JavaEdge

AQS原理浅析关于Lock及AQS的一些补充:羊群效应

4676
来自专栏皮皮之路

【JDK1.8】JUC——AbstractQueuedSynchronizer

3468
来自专栏Android机器圈

Java设计模式总汇二(小白也要飞)

PS:上一篇我介绍了适配器设计模式、单例设计模式、静态代理设计模式、简单工厂设计模式,如果没有看过第一篇的小火鸡可以点这个看看http://www.cnblog...

3499
来自专栏Linyb极客之路

并发编程之各种锁的简介

一、公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁。 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优...

3816
来自专栏大闲人柴毛毛

轻量级线程池的实现

写在前面 最近因为项目需要,自己写了个单生产者-多消费者的消息队列模型。多线程真的不是等闲之辈能玩儿的,我花了两个小时进行设计与编码,却花了两天的时间调试与运...

5224

扫码关注云+社区

领取腾讯云代金券