自定义同步组件
自定义同步组件主要是通过使用队列同步器(AQS)来实现的。队列同步器的子类被推荐定义为自定义同步组件的静态内部类。队列同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的方法来供自定义同步组件使用。
AQS的使用者需要继承AQS并重写指定的方法,随后将AQS组合在自定义同步组件中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法,实现未获取到锁的线程能够挂起,释放锁时能够唤醒其他的线程。(结合最后的例子来理解)
同步器提供了三个方法来访问或修改同步状态:
getState():获取当前同步状态
setState(int newState): 设置当前同步状态
compareAndSetState(int expect, int update) :使用CAS来设置当前状态,保证原子性
同步器可供重写的方法:
| 方法名称 | 描述 |
|---|---|
| protected boolean tryAcquire(int arg) | 独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后进行CAS设置同步状态 |
| protected boolean tryRelease(int arg) | 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态 |
| protected int tryAcquireShared(int arg) | 共享式获取同步状态,返回大于等于0的值,表示获取成功,反之,获取失败 |
| protected boolean tryReleaseShared(int arg) | 共享式释放同步状态 |
| protected boolean isHeldExclusively() | 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所占有 |
同步器提供的模板方法:
| 方法名称 | 描述 |
|---|---|
| void acquire(int arg) | 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将会进入同步队列等待,该方法将会调用重写的tryAcquire(int arg)方法 |
| void acquireInterruptibly(int arg) | 与acquire(int arg)相同,但是该方法响应中断,当前线程未获取到同步状态而进入同步队列中,如果当前线程被中断,该方法会抛出InterruptedException并返回 |
| boolean tryAcquireNanos(int arg, long nanos) | 在acquireInterruptibly(int arg)基础上增加了超时限制,如果当前线程在超时时间内没有获取到同步状态,那么将返回false,如果获取到了返回true |
| void acquireShared(int arg) | 共享式的获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式获取的主要区别是在同一时刻可以有多个线程获取到同步状态 |
| void acquireSharedInterruptibly(int arg) | 与acquireShared(int arg)相同,该方法响应中断 |
| boolean tryAcquireShareNanos(int arg, long nanos) | 在上一条的基础上增加了超市限制 |
| boolean realease(int arg) | 独占式地释放同步状态,该方法会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒 |
| boolean releaseShared(int arg) | 共享式地释放同步状态 |
| Collection<Thread> getQueuedThreads() | 获取等待在同步队列上的集合 |
根据上面两个表格提供的方法可以自定义同步组件,根据需要从表一选择方法重写,根据需要调用表二的方法。下面给出共享锁(允许两个线程共享)的例子,独占锁做法相同。
/**
共享锁
*/
package com.jazon.AQS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class TwinsLock implements Lock {
private final Sync sync = new Sync(2);
@Override
public void lock() {
// 调用队列同步器的模板方法
sync.acquireShared(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
// TODO Auto-generated method stub
}
@Override
public boolean tryLock() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
// TODO Auto-generated method stub
return false;
}
@Override
public void unlock() {
// 调用队列同步器的模板方法
sync.releaseShared(1);
}
@Override
public Condition newCondition() {
// TODO Auto-generated method stub
return null;
}
/*静态内部类继承队列同步器,因为是共享的,所以重写它的tryAcquireShared及tryReleaseShared*/
private static final class Sync extends AbstractQueuedSynchronizer {
public Sync(int count) {
// TODO Auto-generated constructor stub
if (count <= 0) {
throw new IllegalArgumentException("count must large than zero.");
}
setState(count);
}
public int tryAcquireShared(int reduceCount) {
for(;; ) {
int current = getState();
int newCount = current - reduceCount;
if (newCount < 0 || compareAndSetState(current, newCount)) {
//newCount < 0 说明锁被拿完了,CAS是为了保证代码的原子性,这样子newCount的值才等于state的值
//同步器的模板方法将根据newCount的值选择是否挂起线程 <0说明要挂起当前尝试的线程,大于等于0说明不用
return newCount;
}
}
}
public boolean tryReleaseShared(int returnCount) {
for(;;) {
int current = getState();
int newCount = current + returnCount;
if (compareAndSetState(current, newCount)) {
return true;
}
}
}
final ConditionObject newCondition() {
return new ConditionObject();
}
}
}
本文介绍如何利用队列同步器(AQS)实现自定义同步组件,包括独占锁和共享锁的实现方式。详细解释了AQS提供的核心方法,并提供了一个共享锁的具体实现案例。
714

被折叠的 条评论
为什么被折叠?



