美文网首页
手动实现可重入锁

手动实现可重入锁

作者: 瓢鳍小虾虎 | 来源:发表于2021-01-13 11:20 被阅读0次

通过下面的代码,除了理解可重入锁的原理,还可以更好的理解锁的争夺是基于CAS,为什么是不公平锁,为什么是悲观锁。
虽然synchronized关键字声明的锁是由jvm实现的,但是重量级锁的原理大同小异。synchronize不同的地方是,有个从偏向锁到重量级锁转变的过程,在重量级锁之前,锁的竞争完全由CAS决定。

class CustomReentrantLock implements Lock { // 自定义实现可重入锁
    private AtomicReference<Thread> owner = new AtomicReference<>(); // 当前线程
    private LinkedBlockingDeque<Thread> waiters = new LinkedBlockingDeque(); // 等待队列
    private AtomicInteger count = new AtomicInteger(0); // 计算上锁次数

    /**
     * 先tryLock,如果不成功则放入阻塞队列
     * 如果当前线程在阻塞队列头部,则tryLock,成功则从阻塞队列头部取出,否则挂起
     *
     */
    @Override
    public void lock() { // park unpark方法与wait notify 最大的不同是不会死锁, 2组api不能混着用,无效果
        if(!tryLock()) {
            waiters.offer(Thread.currentThread()); // 加入等待队列尾部

            while (true) { // 使用wile循环+tryLock实现自旋CAS操作,同时只要没获取就不会跳出循环,也就不会去执行锁内的代码,从而实现了锁控制
                Thread head = waiters.peek(); // 查看头节点

                if(head == Thread.currentThread()) {
                    if(tryLock()) {
                        waiters.poll(); // 获取到锁,从队列移除头元素
                        return; // 跳出循环
                    } else {
                        LockSupport.park(); // 没获得到锁,挂起当前线程
                    }
                } else {
                    LockSupport.park();
                }
            }
        }
    }

    /**
     * 根据count是否为0判断锁是否被占用
     * 被占用则根据owner判断是否可重复上锁,如果当前线程不是owner则进入waiters队列
     * 没被占用则使用CAS上锁
     * @return
     */
    @Override
    public boolean tryLock() {
        int ct = count.get();
        if (ct == 0) {
            if (count.compareAndSet(ct, ct + 1)) {
                owner.set(Thread.currentThread());
                return true;
            }
        } else {
            if (owner.get() == Thread.currentThread()) {
                count.set(ct + 1);
                return true;
            }
        }

        return false;
    }

    @Override
    public void unlock() { // 释放锁,然后唤起阻塞队列头部的线程,此时参与竞争锁的除了waiter队列的线程还有新创建的线程,这就是非公平锁的原因
        if (tryUnlock()) {
            Thread head = waiters.peek();

            if (head != null) {
                LockSupport.unpark(head);
            }
        }
    }

    /**
     * 如果owner不是当前线程就抛异常
     * 如果owner是当前线程,count计数减1,如果计数为0,使用CAS清空当前owner的值并且返回true
     */
    public boolean tryUnlock() { // unlock的前提:当前线程一定是有锁的持有者,否则报错。只要锁持有者不释放锁,其他线程永远不能获得
        if(Thread.currentThread() != owner.get()) {
            throw new IllegalMonitorStateException();
        } else {
            int c = count.get();
            int nextc = c - 1;
            count.set(nextc);
            if (nextc == 0) {
                owner.compareAndSet(Thread.currentThread(), null);
                return true;
            } else {
                return false;
            }
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

相关文章

网友评论

      本文标题:手动实现可重入锁

      本文链接:https://www.haomeiwen.com/subject/hhruaktx.html