java多线程共享queue_java多线程——AbstractQueuedSynchronizer详解

大家对于AbstractQueuedSynchronizer简称AQS,或许比较陌生,但是应该对可重入锁ReentrantLock,读写锁ReentrantReadWriteLock,信号量Semaphore应该比较熟悉,其实他们的底层实现就是依赖于AQS提供的服务。AQS主要功能就是提供多线程之间同步访问的功能,这里面就涉及多线程之间等待,唤醒,排队。

主要实现思路和架构:

AQS主要的实现思路,主要使用一个volatile int state(代表共享资源)和FIFO的双端队列来确保线程之间的并发等待,唤醒等功能。当前线程通过获取state状态信息,如果获取成功,那么就获得执行权直接运行,如果获取失败,那么直接将线程和状态信息,构造成一个Node节点,放入到双端队列中,阻塞当前线程。如果获取资源的线程执行执行完毕,将释放state,那么将唤醒等待的线程,去获取资源。

如图所示:

2b368b4e96906375fc0c6f1f11acead4.png

state状态获取

state状态声明是volatile,从之前的文章可以知道,该变量的修改对各个线程是可见的。对于state的操作方法,主要有getState(),setState(int newState),compareAndSetState(int expect, int update).

b5c32bf6c112a00623cbbf06109ee09c.png

其中unsafe.compareAndSwapInt(this, stateOffset, expect, update);compareAndSwapInt这里采用乐观锁的技术,当多个线程尝试使用CAS去更新同一个变量时,只有一个线程成功,其他线程都会失败,但是允许失败的线程不断尝试。CAS 操作中包含三个操作数 —— 需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。这里分别对应到stateOffset,expect,update,stateOffset初始化就是state的值。这样做效率比较高一些。

acquire(int)

2a3b368048e89e11d6b1c35e829db8e0.png

tryAcquire尝试去获取锁,成功则返回true,如果失败,那么则调用addWaiter(),以独占式的方式添加到等待队列中,acquireQueued()使线程在等待队列中获取资源,如果获取到资源则返回false。如果在整个等待资源的过程中被中断过,则返回true,否则返回false。selfInterrupt()执行线程中断tryAcquire(int)

3cb39028497d1002aae1655608a6bd4d.png

它的实现是交给子类的具体实现。AQS只是定义了一个框架,tryAcquire的实现交给子类来根据自己的策略实现,框架自身不定义。比如ReentrantLock就有自己的实现,实现具体的公平策略还是非公平策略

release(int)

release(int)方法是在独占模式下,它是AQS提供的最顶层入口,释放资源,如果彻底释放state=0,那么将会触发等待队列中的其他线程获取资源。

088b033e6af19f9c12c53bb490059aea.png

tryRelease()方法与tryLock()方法一样,都是由子类来实现,自身不提供实现。

6bd71d2fd90660b008c70ee400f7490f.png

unparkSuccessor(Node),主要是唤醒等待队列中的下一个线程,从代码的实现上,这里唤醒的不一定是自己的下一个节点,而是下一个可以被唤醒的线程。调用unpark()实现

6fc02957d4040aba0595878173daf749.png

介绍完独占模式下的抢占,接着介绍下共享式的抢占。

acquireShared(int)

cd062214b273f56e15a87f88011f7a5b.png

它会获取指定arg个数量的资源,获取成功则直接返回,否则进入等待队列,直到获取到资源为止,整个过程忽略中断,tryAcquireShared依旧是由子类实现,doAcquireShared函数的意义,进入等待队列的方式,是追加到队列尾部,一直到有其他线程唤醒他,相比而言,这里保证了顺序

releaseShared(int)

9326a4ca237ea941b125ef6e9aed4ab4.png

共享模式下释放资源的最外层接口,tryReleaseShared接口依旧子类实现,释放资源完毕之后,唤醒等待队列中的线程,doReleaseShared()的实现,为了实现共享,他的唤醒是唤醒当前节点之后的等待节点线程。

总结

了解AQS的机制,作为java同步实现基础一个类,核心数据结构,state和双端队列了解独占模式和共享模式下的原理,为之后的学习做好准备PS:您的点赞,关注,收藏,都是给我最大的支持,谢谢

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值