什么是队列同步器
我们在并发包中用到的锁或者其他同步组件都是基于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();
}
}
}