学习过AQS之后,对其实现有一些了解了,虽然jdk中已经提供了很多同步工具,但是为了锻炼代码能力,完全处于学习的目的分析这些同步工具的设计方法,并结合AQS实现简单版本的同步工具。下面会通过AQS实现一个信号量同步工具,后续还会有其他同步工具的实现。
首先需要了解AQS的基本用法,既然是第一篇那么先介绍以下如何使用AQS。高性能锁有以下几个需要关注的点:共享变量、互斥访问、查看持有锁的线程、自旋、等待队列、公平性。AQS中围绕这些点进行的封装,提供了一些很方便的接口,我们只需要借助以下这些接口即可完成特殊功能的同步工具。
// 自定义同步器需要实现的接口
// 获得锁的具体实现,只考虑共享变量怎么设置为多少,是否能重入、是否考虑公平性,其他的都交给AQS
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 释放锁的具体实现
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 共享锁
/**
* 可以看到共享模式下返回值是int类型,表示资源的数量,所以该返回值可以表示两层意思:
* 1. 是否获得锁,大于0就表示获得锁
* 2. 当前线程获得锁之后,是否还有剩余资源,用于唤醒其他等待的的线程
* 因此在实现该方法时首先判断资源量是否足够,如果有足够资源才能使用CAS自旋操作获竞争锁
**/
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
// 共享锁
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
// 实现同步器的辅助方法
// 访问共享变量,表示锁状态
protected final int getState() {
return state;
}
// 互斥设置共享变量
protected final boolean compareAndSetState(int expect, int update) {
return STATE.compareAndSet(this, expect, update);
}
// 设置当前线程持有锁,用来实现重入锁
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
// 查看当前是否有线程等待锁,用来实现公平锁
public final boolean hasQueuedPredecessors() {
}
// 同步器对外接口
// 申请锁
public final void acquire(int arg) {
}
// 释放锁
public final boolean release(int arg) {
}
public final void acquireShared(int arg) {
}
public final boolean releaseShared(int arg) {
}
下面来考虑实现信号量
- 使用组合模式:Semophore不直接基础与AQS而是通过组合其子类完成同步的功能
- Semaphore允许指定数量的线程并发,因此使用AQS的共享锁
- Semaphore提供公平锁,结合hasQueuedPredecessors()接口实现公平锁
package com.xp.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class Semaphore {
final Sync sync;
public Semaphore(int permits) {
this.sync = new NonFairSync(permits);
}
public Semaphore(int permits, boolean fair) {
this.sync = fair ? new FairSync(permits) : new NonFairSync(permits);
}
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits);
}
@Override
protected boolean tryReleaseShared(int release) {
for (;;) {
int cur = getState();
int remain = cur + release;
if (compareAndSetState(cur, remain)) {
return true;
}
}
}
}
static final class NonFairSync extends Sync {
public NonFairSync(int permits) {
super(permits);
}
@Override
protected int tryAcquireShared(int acquire) {
for (;;) {
int cur = getState();
int remain = cur - acquire;
if (remain < 0 || compareAndSetState(cur, remain)) {
return remain;
}
}
}
}
static final class FairSync extends Sync {
public FairSync(int permits) {
super(permits);
}
@Override
protected int tryAcquireShared(int acquire) {
for (;;) {
// 判断是否有其他线程等待锁
if (hasQueuedPredecessors()) {
return -1;
}
int cur = getState();
int remain = cur - acquire;
if (remain < 0 || compareAndSetState(cur, remain)) {
return remain;
}
}
}
}
static class Test {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2, true);
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 1; i < 11; i++) {
int finalI = i;
executor.execute(() -> {
System.out.println(String.format("线程 %d 申请锁 %d", finalI, System.currentTimeMillis()));
try {
semaphore.acquire();
} catch (InterruptedException e) {
System.out.println(String.format("线程 %d 申请锁被中断 %d ", finalI, System.currentTimeMillis()));
return;
}
System.out.println(String.format("线程 %d 获得锁 %d", finalI, System.currentTimeMillis()));
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
// no op
}
semaphore.release();
System.out.println(String.format("线程 %d 释放锁 %d", finalI, System.currentTimeMillis()));
});
}
executor.shutdown();
}
}
}