架构系列——通过ReentrantLock源码分析给对象上锁的原理

 作者专注于Java、架构、Linux、小程序、爬虫、自动化等技术。 工作期间含泪整理出一些资料,微信搜索【javaUp】,回复 【java】【黑客】【爬虫】【小程序】【面试】等关键字免费获取资料。

目录

一、整体结构

二、三个内部类

1.Sync

2.NonfairSync

3.FairSync

三、主要方法

1.lock()方法

2.tryLock()方法

3.unlock()方法

4.lockInterruptibly()方法

四、其它

1.公平锁与非公平锁的区别

2.重入锁的实现原理


一、整体结构

该类位于java.util.concurrent.locks包下

首先看一下结构:

ReentrantLock中:

3个类:Sync、NonfairSync、FairSync

若干个方法:lock()、tryLock()、unlock()、acquireInterruptibly(int arg)等

具体流程 

二、三个内部类

1.Sync

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

      	//抽象lock方法
        abstract void lock();

        //该TryAquire默认给非公平锁使用
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //判断锁的状态
            int c = getState();
            //锁为空闲
            if (c == 0) {
            	//进行CAS操作,设置当前线程持有锁
                if (compareAndSetState(0, acquires)) {
                	//具体实现原理接下来再讲,意思是设置当前持有锁的对象为此线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前线程已获得锁,则进行重入
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
		
		//释放锁
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //释放锁的线程必须为持有锁的线程
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
            	//state为0才能释放锁
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
		
		//如果当前线程同步是以独占的方式进行的,则返回true
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
	
		//新生成的条件
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class
		//获得占用同步状态的线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
		
		//获得当前线程持有锁的数量,即重入次数
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
		
		//是否被锁定
        final boolean isLocked() {
            return getState() != 0;
        }

         //自定义饭序列化逻辑
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

2.NonfairSync

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

         //首先基于AQS进行CAS操作,将0->1.若成功,则获取锁成功
         //否则,执行AQS的正常同步状态获取逻辑
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

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

3.FairSync

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

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

        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;
        }
    }

三、主要方法

注:流程图省略了其他相关类的一些方法,只讲ReentrantLock类里面的方法,比如AQS(AbstractQueuedSynchronizer)类里面的acquere方法中省略了acquireQueued()selfInterrupt()。

如果tryAcquire(arg)返回成功,则说明当前线程成功获取了锁(第一次获取或者重入),由取反和&&可知,整个流程到这结束,只有当前线程获取锁失败才会执行后面的判断。addWaiter(Node.EXCLUSIVE)
部分代码描述了当线程获取锁失败时如何安全的加入同步等待队列。这部分代码可以说是整个加锁流程源码的精华,充分体现了并发编程的艺术性。

1.lock()方法

    public void lock() {
        sync.lock();
    }

1.1 如果该lock是NonfairSync类的实例,则进入NonfairSync的lock()里:

最底层还是进入nonfairTryAcquire()方法里

1.2 如果该lock是FairSync类的实例,则进入FairSync的lock()里:

最底层是进入tryAcquire()方法里

2.tryLock()方法

    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

一步到位,直接调用nonfairTryAcquire()方法

3.unlock()方法

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

4.lockInterruptibly()方法

    public void lockInterruptibly() throws InterruptedException {
        //调用父类AbstractQueuedSynchronizer里的方法
        sync.acquireInterruptibly(1);
    }

    //这是父类AbstractQueuedSynchronizer的方法
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        //中断线程,如果成功中断,则抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

这是Lock锁和synchronized锁相比独有的特性之一,获取锁的线程是可以被中断的,而且中断的时候抛出异常;

四、其它

由上面的图可知,ReentrantLock里面的最最基本的方法是tryAcquire()nonfairTryAcquire()tryRelease()

把这两个方法搞懂了,这个类也就明白很大一部分了

另外要理解两点:

1.公平锁与非公平锁的区别

FairSync中整个tryAcquire()代码只比nonfairTryAcquire()方法多了这么一行!!!

主要判断:是否有其它线程在队列的最前面。

以此来保证有序性,也就是先来后到!

这就是公平!

而不公平就是可以插队拿到锁!

2.重入锁的实现原理

1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;

2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();//获取当前线程实例
    int c = getState();//获取state变量的值,即当前锁被重入的次数
    if (c == 0) {   //state为0,说明当前锁未被任何线程持有
        if (compareAndSetState(0, acquires)) { //以cas方式获取锁
            setExclusiveOwnerThread(current);  //将当前线程标记为持有锁的线程
            return true;//获取锁成功,非重入
        }
    }
    else if (current == getExclusiveOwnerThread()) { //当前线程就是持有锁的线程,说明该锁被重入了
        int nextc = c + acquires;//计算state变量要更新的值
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);//非同步方式更新state值
        return true;  //获取锁成功,重入
    }
    return false;     //走到这里说明尝试获取锁失败
}


posted @ 2021-08-28 10:31  前方一片光明  阅读(125)  评论(0编辑  收藏  举报