ReentrantLock学习笔记

ReentrantLock学习笔记

前言

java中提供了2种锁,一种是synchronized(内置锁,轻量级锁,重量级锁,偏向锁都是指的这种锁),另外一种是ReentrantLock,也就是我们常说的可重入锁。synchronized锁的是对象,而ReentrantLock锁的是线程,ReentrantLock更为复杂,锁的粒度比较细,使用不当,例如lock了,但是没有使用unlock释放锁,容易产生死锁问题。本文主要讲述ReentrantLock。

ReentrantLock

描述

ReentrantLock支持2种锁模式,公平锁和非公平锁,默认的是非公平锁(减少了线程切换,效率会更高一些),具体使用公平锁还是非公平锁,在ReentrantLock构造的时候,用户可以控制。
ReentrantLock的结构和类图:
这里写图片描述
sync是ReentrantLock的同步器,继承AbstractQueuedSynchronizer,结构见下:
这里写图片描述
ReentrantLock使用AbstractQueuedSynchronizer实现了同步队列的管理,具体的同步操作如加锁,解锁,通过调用同步器方法来实现,加锁和解锁是的一些业务逻辑,如可重入的处理,非公平锁的获取锁的操作处理等有自己定义的NonfairSync和FairSync来处理。

初始化

ReentrantLock的初始化,默认为非公平锁,用户可以根据需求自行定义锁的模式。

    public ReentrantLock() {
    //默认是非公平锁
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        //用户根据需求设置
        sync = fair ? new FairSync() : new NonfairSync();
    }

非公平锁

非公平锁顾名思义不是公平的,公平的情况是,一个线程在获取锁的时候,需要进入队列中进行排队,FIFO,先到先得,按顺序处理,非公平的情况是,线程在获取锁的时候,进入队列排队之前,可以尝试获取锁,如果能获取到,就获得执行的机会,而不用顾忌队列中已经排队的线程,也就是说,a持有锁,队列中已经有的线程b,c,d,当前e尝试获取锁,如果这个时间点刚好a释放了锁,state为0,e刚好通过CAS,将锁独占,获得了锁,这是b,c,d还要继续在队列中等待锁的释放,对b,c,d线程来说,是不公平的。

/**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

nonfairTryAcquire,非公平锁模式下锁的尝试获取

/**
         * Performs non-fair tryLock.  tryAcquire is
         * implemented in subclasses, but both need nonfair
         * try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取同步状态,如果为0,就会尝试获取
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    //获得锁,将独占线程,设置为自己,然后正常返回
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                //此处就是ReentrantLock的可重入锁的机制,如果独占线程是自己,state累加,这里要注意,虽然锁可重入,但是占有锁的线程多次获取锁,就一定要多次释放锁,获取锁的操作和释放锁的操作要一一对应,保证state能归0,避免死锁
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

公平锁

公平锁在上一章节已经进行了描述,这里不做详述,看一下公平锁的实现。

 /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //判断线程是不是位于队列头,如果位于头部,尝试获取锁,获取成功,将独占线程设置为自己
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
            //可重入锁部分,跟非公平锁一致
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

锁的释放

public void unlock() {
        sync.release(1);
    }

public final boolean release(int arg) {
        if (tryRelease(arg)) {
        //释放锁后,判断队列中的head节点,如果合法,就唤醒它,去尝试获取锁
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
 protected final boolean tryRelease(int releases) {
 //递减state变量,主要用来处理可重入锁产生多state变量递增,state为0,表示锁已经释放,可以被其他线程获取
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

其实在ReentrantLock的实现中,最重要的部分就是AQS,AQS同步器通过无锁的方式,基于CAS+队列的方式,提供了一种同步方案,AQS实现机制还是比较复杂的,后续有时间在深入源码分析一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值