0
点赞
收藏
分享

微信扫一扫

6.手把手教你使用AQS实现互斥锁

什么是队列同步器

我们在并发包中用到的锁或者其他同步组件都是基于AQS构建的。

AQS 里面提供了三个基础方法:setState(),getState(),compareAndSetState();

实现者(自己扩展锁或开发一个同步组件)通过调用这几个方法就可以保证线程安全的,这也是DougLea最终的期望。

同步器既可以支持独占式获取锁,也支持获取共享锁。

锁与同步器之间的关系:锁是面向使用者的,它对用户隐藏了底层的实现细节;

同步器是面向锁的,它给锁的实现提供了基础服务。

同步基础方法

getState()

获取当前同步状态,返回一个整形数值

setState()

设置状态,接受一个整形数值

compareAndSetState()

利用CAS 线程安全的设置状态

同步器中可重写的方法

tryAccquire()

独占式获取锁,重写使需要查询当前状态并且判断同步状态是否符合预期,然后再通过CAS设置状态值

tryRelease()

独占式释放锁

tryAccquireShared()

共享式获取同步状态,返回大于等于0 表示获取成功

tryReleaseShared()

共享式释放同步状态

isHeldExclusively()

当前锁是否被当前线程独占

同步器中的模版方法

accquire()

独占式获取同步状态如果获取锁成功,则返回,否则进入到同步队列等待,该方法会调用tryAccquire()尝试获取锁,

acquireInterruptibly()

该方法会响应中断,

tryAcquireNanos()

在accquireInterruptibly()基础上加了超时时间,如果在指定时间范围内没有拿到,则返回false

acquireShared()

共享式获取同步状态,当前锁会被多个线程共享

acquireSharedInterruptibly()

响应中断

tryAcquireSharedNanos()

在accquireSharedInterruptibly()基础上添加超时时间

release()

释放独占锁

releaseShared()

释放共享锁

getQueuedThreads()

获取等待在同步队列上的集合

实现一个互斥锁

首先给大家一个思路,实现互斥锁的基本流程

  • 继承Lock 接口,对外提供统一的获取锁和释放锁的方法
  • 在内部实现一个AQS,通过AQS来实现锁的获取和释放
package com.ams.thread.lesson5;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
* 关注微信公众号"AI码师"获取项目源码及2021面试题一套
*
* @author: AI码师
* Date: 2021/12/30 6:09 上午
* Description:
*/
public class MyMutexLock implements Lock {
private final LockAQS lockAQS = new LockAQS();

@Override
public void lock() {
lockAQS.acquire(1);
}

@Override
public void lockInterruptibly() throws InterruptedException {
lockAQS.acquireInterruptibly(1);
}

@Override
public boolean tryLock() {
return lockAQS.tryAcquire(1);
}

@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return lockAQS.tryAcquireNanos(1, unit.toNanos(time));
}

@Override
public void unlock() {
lockAQS.release(1);
}

@Override
public Condition newCondition() {
return lockAQS.newCondition();
}

private static class LockAQS extends AbstractQueuedSynchronizer {
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}

/**
* 重新获取锁的逻辑,只有当前同步状态为0,才允许获取成功
*
* @param arg
* @return
*/
@Override
protected boolean tryAcquire(int arg) {
// 通过cas机制去设置新值,设置成功,则代表同步状态为0
if (compareAndSetState(0, 1)) {
return true;
}
return false;
}

@Override
protected boolean tryRelease(int arg) {
// 首先判断当前state 如果当前状态值不是1 说明当前锁没有被人获取
if (getState() != 1) {
return false;
}
// 判断当前获取锁的线程是否是当前线程 如果是的,则可以释放锁,否则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
setState(0);
setExclusiveOwnerThread(null);
return true;
}

Condition newCondition() {
return new ConditionObject();
}
}

}

使用互斥锁

package com.ams.thread.lesson5;

import cn.hutool.core.thread.ThreadUtil;
import lombok.extern.slf4j.Slf4j;

/**
* 关注微信公众号"AI码师"获取项目源码及2021面试题一套
*
* @author: AI码师
* Date: 2021/12/30 6:08 上午
* Description:
*/
@Slf4j
public class Example16 {
private static MyMutexLock myMutexLock = new MyMutexLock();

public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
static class Thread1 implements Runnable {

@Override
public void run() {
myMutexLock.lock();
log.info("Thread1 获取到锁");
ThreadUtil.sleep(5000);
myMutexLock.unlock();
}
}
static class Thread2 implements Runnable {

@Override
public void run() {
myMutexLock.lock();
log.info("Thread2 获取到锁");
ThreadUtil.sleep(5000);
myMutexLock.unlock();
}
}
}


举报

相关推荐

0 条评论