介绍
本文将介绍如何应用AbstractQueuedSynchronizer实现一个简单的Lock锁。至于AbstractQueuedSynchronizer的原理在博主其他文章中有介绍,本文重点讲述其应用。AbstractQueuedSynchronizer使用一个队列维护所有的等待锁释放的线程,队列中的每个结点通过自旋的方式来争抢锁,当同步结点释放锁,唤醒next结点。在自旋中,该线程序判断当前结点的前驱是否为队头结点,以及是否能够通过tryAcquire(int)设置同步状态,若是则将其设置成队头结点。
通过上述的介绍可知,锁的获取顺序和队列中结点的顺序一致,因此保证了公平锁的锁获取顺序顺序。在公平锁中,通过tryAcquire(int)获取同步状态方法中,设置同步状态之前,先通过判断是否有在排队的结点,若有,则先处理前面的,并将其加入到同步队列中。但在非公平锁中,会先直接尝试获取锁,即不会判断队列中是否还有没有排队的线程就直接尝试获取锁,获取不到才将其加入到同步队列中。
AbstractQueuedSynchronizer API
AbstractQueuedSynchronizer是一个抽象类,其定义了一套模版,需实现者重写其tryAcquire(int)、tryRelease(int)、isHeldExclusively()等方法。
在同步器中,会调用上述方法来实现同步器。开发者需维护一个同步状态即可完成Lock的定制。
同步器主要提供的API如下:
一个简单的Lock锁实现
本文通过重写抽象同步器的抽象方法来实现一个锁。
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 Mutex implements Lock{
//创造一个同步器
//该同步器用一个state来维护,当state表示同一时间当前同步状态的线程数量
private static class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if(compareAndSetState(0, arg)){ //CAS操作的原子性,若当前线程设置成1,其他线程无法设置成功
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if(getState() == 0)throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0); //由于获取到了锁,因此这一步无需通过CAS操作保证原子性
return true;
}
//是否处于占用状态
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
//返回一个Condition
Condition newCondition(){
return new ConditionObject();
}
}
private final Sync sync = new Sync(); //final域能保证在同一个时间内只被一个线程初始化
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
public boolean isLocked(){
return sync.isHeldExclusively();
}
//判断当前同步队列中是否还有等待获取锁
public boolean hasQueuedThreads(){
return sync.hasQueuedThreads();
}
public boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException{
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}
使用自定义锁:
import java.util.concurrent.locks.Lock;
public class Test {
public static void main(String[] args) {
final Lock lock = new Mutex();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try{
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + "-"+ i);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally{
lock.unlock();
}
}
},"Thread1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try{
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + "-" + i);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally{
lock.unlock();
}
}
},"Thread2");
t1.start();
t2.start();
}
}
输出结果:
Thread1-0
Thread1-1
Thread1-2
Thread1-3
Thread1-4
Thread1-5
Thread1-6
Thread1-7
Thread1-8
Thread1-9
Thread2-0
Thread2-1
Thread2-2
Thread2-3
Thread2-4
Thread2-5
Thread2-6
Thread2-7
Thread2-8
Thread2-9