并发编程(五):AQS之重入锁ReentrantLock

一,AQS源码博文:并发编程:AbstractQueuedSynchronizer源码分析

二,ReentrantLock重入锁基本介绍

    1,类图

        * 从图中可以看到,ReentrantLock是顶层锁接口Lock的实现类,并实现了Lock所定义的关于锁操作的基本API

        * ReentrantLock内部定义了三个有继承关系的内部类,Sync,FairSync,NonfairSync

        * 其中Sync继承自AQS类,是ReentrantLock底层通过AQS进行线程调度的基础和扩展

        * FairSync和NonfairSync分别继承自Sync类,是ReentrantLock对于公平锁和非公平锁的不同实现

    2,重入锁

        a,重入锁,表示重新进入的锁。也就是如果线程1通过调用lock方法获取了锁之后,再次调用lock,是不会阻塞等待获取锁的(如果等待,直接死锁),只需要增加锁的重入次数(即state)就可以(getHoldCount())。同时,在释放锁时,也不是直接释放掉当前线程的锁,而是对重入次数(即state)递减。当state为0时,再进行锁释放并唤醒下一个队列节点!如下例子

package com.gupao.concurrent;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 独占锁演示
 * @author pj_zhang
 * @create 2019-10-01 14:28
 **/
public class ExclusiveTest {

    // 定义全局重入锁对象
    private static final ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            // 遍历并启动100道线程
            Thread thread = new Thread(() -> {
                try {
                    // 线程内部加锁并沉睡业务秒数,模拟获取到锁后不立即释放
                    // 多次加锁,模拟重入锁
                    reentrantLock.lock();
                    reentrantLock.lock();
                    reentrantLock.lock();
                    Thread.sleep(3000);
                    System.out.println(
                            Thread.currentThread().getName() + "获取锁,执行时间: "
                                    + System.currentTimeMillis());
                    // 通过getHoldCount()获取重入次数
                    System.out.println("当前重入次数:" + reentrantLock.getHoldCount());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // finally块中释放锁
                    // 此处只释放一次,模拟线程依旧阻塞,则不会唤醒阻塞线程,整个流程在此阻塞
                    reentrantLock.unlock();
                    System.out.println("释放一次后重入次数:" + reentrantLock.getHoldCount());
                }
            }, "THREAD_" + i);
            thread.start();
            System.out.println(thread.getName() + "启动成功...");
        }
    }
}

    3,常用API

        * 构造器

// 默认初始化非公平锁
public ReentrantLock() {
	sync = new NonfairSync();
}
// 通过参数指定,构造公平锁或者非公平锁
public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync();
}

        * 常用方法

// 加锁
void lock();
// 加锁,中断后抛异常
void lockInterruptibly() throws InterruptedException;
// 尝试加锁
boolean tryLock();
// 尝试加锁_带时间
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 解锁
void unlock();
// 获取重入次数
int getHoldCount();
...

三,非公平锁源码解析

    1,构造器

// 默认构造器
public ReentrantLock() {
	sync = new NonfairSync();
}
// 获取有参构造传递false
public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync();
}

    2,尝试加锁源码解析

        2.1,尝试加锁_不带时间

            * tryLock():尝试加锁

public boolean tryLock() {
	// 调用Sync的方法,即父类方法,此处公平锁和非公平锁一致
	return sync.nonfairTryAcquire(1);
}

            * java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire(int acquires)

final boolean nonfairTryAcquire(int acquires) {
	// 获取当前线程
	final Thread current = Thread.currentThread();
	// 获取state,即重入次数
	int c = getState();
	if (c == 0) {
		// state为0,说明锁空闲,通过CAS替换加锁
		if (compareAndSetState(0, acquires)) {
			// 替换成功后,将当前线程设置为独占线程
			setExclusiveOwnerThread(current);
			return true;
		}
	}
	// 如果当前线程就是独占线程,表示重入
	else if (current == getExclusiveOwnerThread()) {
		// 重入次数加上acquires表示的次数
		int nextc = c + acquires;
		if (nextc < 0) // overflow
			throw new Error("Maximum lock count exceeded");
		// 并替换state的值
		setState(nextc);
		return true;
	}
	// 以上如果ruturn说明获取锁成功
	// 否则失败
	return false;
}

         2.2,尝试加锁_带时间

            * tryLock(long timeout, TimeUnit unit):尝试带时间加锁

public boolean tryLock(long timeout, TimeUnit unit)
		throws InterruptedException {
	// 此处调用AQS.tryAcquireNanos尝试加锁,注意将时间转换为纳秒
	return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

            * AQS.tryAcquireNanos() 已经在AQS博文中解析,继续跟踪代码,会发现 tryAcquire() 方法在子类中重新定义,此处只对 tryAcquire() 方法进行分析

            * java.util.concurrent.locks.ReentrantLock.NonfairSync#tryAcquire(int acquires):可以发现 tryAcquire() 中依旧调用 nonfairTryAcquire() 方法尝试获取锁

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

    3,加锁源码解析

        * lock():加锁

public void lock() {
	// 此处调用非公平锁的 lock() 方法
	sync.lock();
}

        * java.util.concurrent.locks.ReentrantLock.NonfairSync#lock():调用非公平锁的 lock() 方法

final void lock() {
	// 替换 state 状态,为0表示没有线程加锁,成功替换为1说明当前线程加锁成功
	if (compareAndSetState(0, 1))
		// 将独占线程指向当前线程
		setExclusiveOwnerThread(Thread.currentThread());
	else
		// 调用AQS的 acquire() 方法进行加锁,参考AQS源码博文
		// 加锁失败后会添加到AQS同步队列
		acquire(1);
}

    4,释放锁源码解析

        * unlock()

public void unlock() {
	// 调用AQS.release方法
	sync.release(1);
}

        * java.util.concurrent.locks.AbstractQueuedSynchronizer#release(int arg):AQS中释放锁,先通过业务代码尝试释放锁,释放成功后唤醒下一个节点

public final boolean release(int arg) {
	// 调用Sync.tryRelease尝试释放锁
	if (tryRelease(arg)) {
		// 释放锁成功后,唤醒同步队列上的下一个节点
		Node h = head;
		if (h != null && h.waitStatus != 0)
			unparkSuccessor(h);
		return true;
	}
	return false;
}

        * java.util.concurrent.locks.ReentrantLock.Sync#tryRelease(int releases):重入锁自定义尝试释放逻辑

protected final boolean tryRelease(int releases) {
	// 对state进行递减,获取递减后的state值
	int c = getState() - releases;
	if (Thread.currentThread() != getExclusiveOwnerThread())
		throw new IllegalMonitorStateException();
	boolean free = false;
	// 如果c为0,表示所有重入已经被全部释放
	if (c == 0) {
		free = true;
		// 将独占线程为空,当前线程不在占有锁
		setExclusiveOwnerThread(null);
	}
	// 对state赋递减后的值
	setState(c);
	return free;
}

四,公平锁源码解析

    1,构造器

// 获取有参构造传递true
public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync();
}

    2,尝试加锁源码解析

        2.1,尝试加锁_不带时间

            * 与公平锁一致,此处翻代码没有非公平锁实现,疑惑ING!!!

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

        2.2,尝试加锁_带时间

             * 之前步骤与公平锁一致,调用最终到 tryAcquire() 调用自定义方法

             * java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire(int acquires):尝试获取锁,注意公平锁与非公平锁尝试获取锁的区别

protected final boolean tryAcquire(int acquires) {
	final Thread current = Thread.currentThread();
	// 获取当前状态
	int c = getState();
	if (c == 0) {
		// hasQueuedPredecessors:判断是否存在等待的前置节点,存在则获取失败
		// 这也是公平锁和非公平锁的最主要区别,默认排到队尾执行,非公平锁直接插队获取锁
		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;
}

        * java.util.concurrent.locks.AbstractQueuedSynchronizer#hasQueuedPredecessors():判断队列中是否存在等待元素

public final boolean hasQueuedPredecessors() {
	// 尾结点
	Node t = tail;
	// 头节点
	Node h = head;
	Node s;
	// 头节点不是尾结点,说明存在多个节点
	// 头节点的next为空,说明头节点正在运行中
	// 或者next节点线程和当前线程不一致,说明存在等待线程
	return h != t &&
		((s = h.next) == null || s.thread != Thread.currentThread());
}

    3,加锁源码解析

        * 公平锁的 lock() 与非公平锁的 lock() 相比,同样是在每次调用 tryAcquire() 时,调用不同的实现。不同的实现已经在上面解析

    4,释放锁源码解析

        * 公平锁与非公平锁一致

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值