第二章 可重入锁——ReentrantLock实现原理

系列文章目录

Java多线程系列
第一章 Java线程池源码详解
第二章 可重入锁——ReentrantLock实现原理


一、LOCK 接口定义

Lock接口是提供加锁操作

方法作用
lock阻塞直到获得锁
lockInterruptibly和lock类似,但可响应中断,抛中断异常
tryLock尝试获得锁,不管是否获取到,立马返回。返回true获得锁,返回false没获得。调用tryLock只会以非公平方式去争抢锁
tryLock(long time, TimeUnit unit)与tryLock类似,获取不到不是立马返回,会等待一段时间
unlock解锁
newCondition相当于syschronized的同步代码块的wait、notify、notifyAll,相比syschronized,ReentrantLock支持多个Condition

二、什么是“可重入”

可重入就是,持有锁的线程可以多次重复加锁。锁不支持可重入很容易造成死锁,线程获得了锁,重入时产生死锁——自己等待自己释放锁。

2.1 ReentrantLock如何实现可重入

  1. 线程调用lock时:锁状态为0表示可加锁,使用CAS将状态设置成1进行锁的争抢。锁重入时,状态自增+1
  2. unlock时,状态自减-1,减到0时,锁释放,唤醒阻塞队列的线程,进行CAS锁争抢
        final boolean tryLock() {
            Thread current = Thread.currentThread();
            // 状态为0表示可获得锁
            int c = getState();
            if (c == 0) {
            	// CAS操作,锁争抢,只有一个线程能成功加锁
                if (compareAndSetState(0, 1)) {
                	// 记录当前持有锁的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (getExclusiveOwnerThread() == current) {
            	// 锁重入啦:持有锁的线程 == 当前线程
            	// 状态自增
                if (++c < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(c);
                return true;
            }
            return false;
        }

所释放:

        protected final boolean tryRelease(int releases) {
        	// 这里releases == 1
        	// 状态自减1
            int c = getState() - releases;
            if (getExclusiveOwnerThread() != Thread.currentThread())
                throw new IllegalMonitorStateException();
            // 减到状态为0,说明锁释放了
            boolean free = (c == 0);
            if (free)
                setExclusiveOwnerThread(null);
            setState(c);
			// 如果返回true,会唤醒阻塞队列的线程
            return free;
        }

三、公平锁与非公平锁

描述优缺点
公平锁所谓公平,就是排队的时候讲究先来后到,先来的先获取锁。优点:不会“线程饥饿”

缺点:系统线程上下文切换次数高很多,从而吞吐量较低
非公平锁不要求先来先获取锁,阻塞队列存在等待锁,新来的线程也可以立马获取到锁。很大程度减少线程上下文切换优点:吞吐量高

缺点:可能存在“线程饥饿”

线程饥饿:线程A在阻塞队列中等待锁,但是锁一直被后面来的线程争抢到锁,导致线程A一直拿不到锁,这是我们说线程A“饥饿”
根据网友测试,并发度较高时,非公平锁的吞吐量比公平锁高100倍

默认是非公平锁

    public ReentrantLock() {
        sync = new NonfairSync();
    }

四 公平锁、非公平锁的实现

4.1 总体类图

公平同步器(FairSync)和 非公平器(NonfairSync)都继承自同步器类(Sync),Sync继承自抽象队列同步器类(AbstractQueuedSynchronizer, AQS)
UML:

+ Public
- Private
# Protected
~ Package
AbstractOwnableSynchronizer
#setExclusiveOwnerThread(thread)
#getExclusiveOwnerThread() : Thread
AbstractQueuedSynchronizer
#status: int
+tryAcquire(acquires) : boolean
+tryRelease(releases) : boolean
Sync
~tryLock() : boolean
~initialTryLock() : boolean
+tryRelease(releases:int) : boolean
FairSync
~initialTryLock() : boolean
+tryRelease(releases) : boolean
NonfairSync
~initialTryLock() : boolean
+tryRelease(releases) : boolean

4.2 Sync类

Sync抽象类继承自AQS类,子类有FairSync和NonfairSync
主要把子类相同的tryRelease、tryLock、lock模板方法进行了实现

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * 进行非公平方式 tryLock.
         */
        @ReservedStackAccess
        final boolean tryLock() {
        	// 以非公平方式进行tryLock
        }

        /**
         * 调用lock方法会先执行该方法,我理解主要是抽一层用于处理锁的重入
         * 如果获取到锁返回true
         */
        abstract boolean initialTryLock();

        @ReservedStackAccess
        final void lock() {
        	// 首先调用initialTryLock,如果加锁失败
            if (!initialTryLock())
            	// 内部调用tryAcquire尝试获得锁,失败则会将线程加入阻塞队列(自旋一定次数后)
                acquire(1);
        }

      

        @ReservedStackAccess
        protected final boolean tryRelease(int releases) {
        	// 上文讲过这个方法
        }
		// 省略其他方法
    }

4.3 NonfairSync类

非公平锁实现,使用CAS去争抢

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
		/**
		* 调用lock方法首先会执行initialTryLock
		*/
        final boolean initialTryLock() {
            Thread current = Thread.currentThread();
            // 直接CAS,体现了锁争抢是非公平的,谁CAS成功,谁加锁成功
            if (compareAndSetState(0, 1)) { // first attempt is unguarded
                setExclusiveOwnerThread(current);
                return true;
            } else if (getExclusiveOwnerThread() == current) {
            	// 处理可重入,上文介绍过
                int c = getState() + 1;
                if (c < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(c);
                return true;
            } else
                return false;
        }

        /**
         * tryAcquire是AQS定义模板方法,尝试获得独占锁
         */
        protected final boolean tryAcquire(int acquires) {
        	// 这里也是CAS去设置state,体现了非公平
            if (getState() == 0 && compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
    }

4.4 FairSync类

公平锁,判断阻塞队列为空,再去争抢锁。
多加了一行代码 !hasQueuedPredecessors(),其他实现和NonfairSync一致。
hasQueuedThreads是AQS提供的方法:查询是否有线程正在等待获取锁

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final boolean initialTryLock() {
            Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            	// 判断阻塞队列为空再去争抢锁
                if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            } else if (getExclusiveOwnerThread() == current) {
                if (++c < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(c);
                return true;
            }
            return false;
        }

        protected final boolean tryAcquire(int acquires) {
        	// 判断阻塞队列为空再去争抢锁
            if (getState() == 0 && !hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值