基于AQS实现不可重入的独占锁:
/**
* 基于AQS实现不可重入的独占锁
* 定义 state == 0:表示锁没有被线程持有。
* state == 1: 表示锁已经被某个线程持有。
* 不可重入锁,则不需要记录持有锁的线程获取锁的次数
* @date 2021/4/4
*/
public class NonReentrantLock implements Lock, Serializable {
// 创建一个Sync来做具体的工作
private final Sync sync = new Sync();
@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 boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
private static class Sync extends AbstractQueuedSynchronizer{
// 锁是否已经被持有
protected boolean isHeldExclusively(){
return getState() == 1; // state == 1 则表示锁已经被线程持有
}
// 尝试获取锁
// @param acquire : 获取的论据,可以代表你喜欢的任何东西
public boolean tryAcquire(int acquires){
assert acquires == 1;
if(compareAndSetState(0,1)){ // CAS获取锁,如果成功获取锁,设置state = 1
setExclusiveOwnerThread(Thread.currentThread()); // 设置拥有锁的线程为当前线程
System.out.println(Thread.currentThread() + "获取锁成功");
return true; // 尝试获取锁成功
}
System.out.println(Thread.currentThread() + "获取锁失败");
return false; // 否则尝试获取锁失败
}
// 尝试释放锁
protected boolean tryRelease(int releases){
assert releases == 1;
if(getState() == 0){ // 如果state == 0 则抛出异常,因为当前没有获取锁,不需要释放锁
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null); // 设置拥有锁的线程为null,即没有线程拥有该锁
setState(0); // 设置state = 0
return true; // 尝试释放锁成功
}
// 提供条件变量接口
Condition newCondition(){
return new ConditionObject();
}
}
}
在上面的代码中,NonReentrantLock定义了一个内部类Sync用来实现具体的锁的操作,Sync继承了AQS。由于我们实现的是独占模式的锁,所以Sync重写了tryAcquire、tryRelease和isHeldExclusively三个方法,并提供newCondition方法来支持条件变量。
使用上面定义的锁实现生产-消费模型:
public class TestNonReentrantLock {
final static NonReentrantLock lock = new NonReentrantLock();
final static Condition notFull = lock.newCondition();
final static Condition notEmpty = lock.newCondition();
final static Queue<String> queue = new LinkedBlockingDeque<String>();
final static int queueSize = 10;
public static void main(String[] args) throws InterruptedException {
Thread producer = new Thread(new Runnable() {
@Override
public void run() {
// 获取独占锁
lock.lock();
try{
// 如果队列满了,则等待。用while而不是if是为了避免虚假唤醒
while(queue.size() == queueSize) {
System.out.println("队列满了,不能生产");
notEmpty.await();
}
// 添加元素到队列
queue.add("ele");
System.out.println("produce an ele");
// 唤醒消费线程
System.out.println(Thread.currentThread() + "唤醒消费者线程");
notFull.signalAll();
}catch (Exception e) {
e.printStackTrace();
}finally {
// 释放锁
lock.unlock();
}
}
});
Thread consumer = new Thread(new Runnable() {
@Override
public void run() {
// 获取独占锁
lock.lock();
try {
// 队列空,则等待。用while而不是if是为了避免虚假唤醒
while(0 == queue.size()){
System.out.println("队列为空,没有元素可消费");
notFull.await();
}
// 消费一个元素
String ele = queue.poll();
System.out.println("consume an ele");
// 唤醒生产线程
System.out.println(Thread.currentThread() + "唤醒生产者线程");
notEmpty.signalAll();
}catch (Exception e) {
e.printStackTrace();
}finally {
// 释放锁
lock.unlock();
}
}
});
// 启动线程
consumer.start();
Thread.sleep(1000); // 让消费者先获取锁,看是否会造成死锁
producer.start();
}
}
控制台输出如下: