Java并发——通过ReentrantLock与Semaphore揭秘AQS独占模式与共享模式

508 阅读3分钟

大家好,这里是淇妙小屋,一个分享技术,分享生活的博主
以下是我的主页,各个主页同步更新优质博客,创作不易,还请大家点波关注
掘金主页
后续会发布更多MySQL,Redis,并发,JVM,分布式等面试热点知识,以及Java学习路线,面试重点,职业规划,面经等相关博客
转载请标明出处!

本文将通过ReentrantLock和Semaphore带你看看AQS的独占模式和共享模式是怎么实现的

  • ReentrantLock实现了AQS的独占模式
  • Semaphore实现了AQS的共享模式

1. ReentrantLock介绍

可重入:任意线程获得锁后能够再次获取该锁而不会被锁阻塞

ReentrantLock实现了AQS的独占模式,是一个可重入锁,还分为 公平锁非公平锁

  • 公平锁:先对锁进行获取请求的线程一定先获得锁
  • 非公平锁

非公平锁的效率高于公平锁

非公平锁可能出现 线程饥饿问题——部分线程迟迟无法获得资源

ReentrantLock

ReentrantLock大多数方法的实现都是Sync及其子类来完成,ReentrantLock只是对外暴露了接口

2. ReentrantLock获得锁

2.1 非公平锁

ReentrantLock

2.2 公平锁

ReentrantLock

2.3 公平锁与非公平锁的不同

FairSync和NonfairSync的 lock() 和 tryAcquire() 逻辑不同

  1. 非公平锁在lock()方法的开始就会尝试去通过CAS修改同步状态以获得锁,公平锁不会

  2. 在自旋时,非公平锁和公平锁都会在前继节点为同步队列首节点时,**调用tryAcquire()**尝试获取锁

    tryAcquire()中,如果state为0,那么非公平锁不会关心节点在同步队列中的位置,直接尝试CAS修改state获得锁;但是非公平锁关心节点的位置,会检查是否有前继节点,如果有,就会放弃

上述2点保证了公平锁一定是——先对锁进行获取请求的线程一定先获得锁,而非公平锁不一定

3. ReentrantLock释放锁

公平锁释放锁与非公平锁释放锁采用同一个逻辑

ReentrantLock非公平锁释放锁

4. Semaphore介绍

Semaphore实现了AQS的共享模式

信号量,用来控制同时访问特定资源的线程数目

初始化时指定信号量(permits)的数目(本质还是AQS的state)

如果线程想要访问一个资源,必须先获得信号量减少,信号量为0时,线程无法访问资源,只能WATING等待信号量>0,

如果使用完资源,释放后,会补充信号量

(之前的ReentrantLock中state=0表示锁可用,state不为0表示锁不可用,Semaphore这里state不为0表示锁可用,state为0表示锁不可用)

Semaphore.

5. Semaphore方法介绍

//尝试获取一个信号量,如果信号量不为0,那么将信号量-1,返回
//如果信号量为0,WAITING直到信号量不为0
//可中断
public void acquire() throws InterruptedException

//尝试获取多个信号量,如果信号量足够,那么将信号量-permits,返回
//如果信号量不够,WAITING直到信号量不为0
//可中断 
public void acquire(int permits) throws InterruptedException
    
//同acquire(),但不可中断
public void acquireUninterruptibly()
    
//同acquire(int permits),但不可中断
public void acquireUninterruptibly(int permits)

//释放一个信号量
public void release()
    
//释放permits个信号量
public void release(int permits)

6. 方法详解

6.1 void acquire(int permits)

底层调用的还是AQS共享模式获取锁的那一套

AQS共享模式获得锁

公平模式——FariSync中的 tryAcquireShared(int acquires)

Semaphore的acquire

非公平模式——NonfairSync中的 tryAcquireShared(int acquires)调用Sync中的 nonfairTryAcquireShared(int acquires)

23

6.2 void release(int requires)

底层调用的还是AQS共享模式释放锁的那一套

AQS共享模式释放锁

Sync实现了 tryReleaseShared(int releases)

Semaphore的tryReleaseShared