并发多线程之JUC原理

在了解了java内置锁(synchronized)之后,我们来了解下自1.5之后java在java.util.concurrent包中提供的并发工具类,大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这个行为的抽象就是基于AbstractQueuedSynchronizer简称AQS,AQS定义了一
套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器。
AQS具有以下特性:

  • 阻塞等待队列
  • 共享/独占
  • 公平/非公平
  • 可重入
  • 允许中断

同时在AQS中维持了两种队列,同步队列和条件队列。

同步等待队列
同步等待队列AQS当中的同步等待队列也称CLH队列,CLH队列是Craig、Landin、Hagersten三人发明的一种基于双向链表数据结构的队列,是FIFO先入先出线程等待队列,Java中的CLH队列是原CLH队列的一个变种,线程由原自旋机制改为阻塞机制。队列结构如下:

在这里插入图片描述

条件等待队列
Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一
起等待某个条件(Condition),只有当该条件具备时,这些等待线程才会被唤
醒,从而重新争夺锁。需要注意的是,在条件队列中的节点的状态都是独占模式。条件等待队列的结构如下:
在这里插入图片描述
AQS通过这两种队列实现了线程的调度和唤醒。

AQS主要属性及主要方法

在大致了解了AQS的特性及对应的队列我们来了解下AQS类内部的主要属性和方法。
AbstractQueuedSynchronizer是为与java.util.concurrent包下的一个抽象类,其继承路径如下图所示:
在这里插入图片描述
从图中我们可以看到,其继承自AbstractOwnableSynchronizer并实现了Serializable接口,那么我们先来看下AbstractOwnableSynchronizer类的实现:

在这里插入图片描述
这是AbstractOwnableSynchronizer类的结构,我们可以看到在该类中定义了一个exclusiveOwnerThread属性,通过名称我们大致了解就是独占线程,同时提供了对应的getter和setter方法,从这儿可以知道AQS是通过这种方式记录占有锁的。接下来我们再来查看AbstractQueuedSynchronizer类的结构,由于该类的机构较复杂我们拿出主要的属性和方法讲解:

  1. state 属性:

    private volatile int state;
    

    之前提到,AQS是可重入的,那么重入的状态就是通过state这个属性记录的,当线程没进入一次锁,则state会加1,当没释放一次锁state会减一,当state为0则证明完全释放锁。

  2. head和tail

    private transient volatile Node head;
    private transient volatile Node tail;
    

    这两个属性就是实现同步等待队列的,head为队列的头节点,tail为队列的尾节点,Node为AbstractQueuedSynchronizer的一个内部类,在该Node节点中维护了prev和next指针用于指向队列的前置节点和后置节点,同时也维护了一个thread属性用于指向当前节点代表的线程,需要注意的是在该队列中,当队列中有数据时,head指向的节点不带表任何线程,就是一个空节点,假设现现在同步队列中有三个节点分别代表线程1、线程2和线程3,那么队列结构如下:
    在这里插入图片描述

  3. 判断当前线程是否独占资源

    protected boolean isHeldExclusively()
    
  4. 独占方式。尝试获取资源,成功则返回true,失败则返回false。

    protected boolean tryAcquire(int arg)
    
  5. 独占方式。尝试释放资源,成功则返回true,失败则返回false。

    protected boolean tryRelease(int arg)
    
  6. 共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

    protected int tryAcquireShared(int arg)
    
  7. 共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

     protected boolean tryReleaseShared(int arg)
    

以上方法均抛出异常,需要其子类去实现从而决定其子类是共享锁还是独占锁。

那么下一篇来梳理一下AQS的源码,大致思路是先梳理相关类的每个方法,然后再通过一个切入点(ReentrantLock的lock方法)从而将整个流程串起来,个人觉得这种方法对整个源码流程可以有更好的把握。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值