基于AQS实现自定义不可重入独占锁

基于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();


    }
}

控制台输出如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值