ReentrantLock源码分析及实现原理

    ReentrantLock可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义。但功能更强大,更灵活,可减少死锁发生的概率。ReentrantLock由最近成功获取锁且未释放锁的线程拥有。

    ReentrantLock提供了公平锁与非公平锁的选择,当构造方法接收一个可选的公平参数且该值为true时,ReentrantLock表示的实现的是一个公平锁,及当ReentrantLock队列中的线程去获取锁的时候,优先队列中等待时间最长的线程,公平锁在多线程访问时表现为很低的吞吐量。   ReentrantLock的UML类图如下:

ReentrantLock的用法:

1.初始化lock的时候传入false,构造一个不公平锁;

2.调用lock()方法获取锁,lock()方法是ReentrantLock的静态内部类Sync的抽象方法,继承至AQS。lock()由FairSync和NonFairSync实现。ReentranLock默认是使用不公平锁-NonFairSync,其实现如下:

final void lock() {
    if (compareAndSetState(0, 1))              
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

    AQS内部维护这一个volatile修饰的state值,用以记录线程是否持有锁。该值为0时表示当前无线程持有锁。当持有锁的线程重入时state累加1。所以lock()第一步就是通过cas将state的状态从0变为1。如果能成功表示当前无线程持有锁,就设置当前线程独占锁。如果当前锁已经被其他线程持有,即state不为0,则调用acquire()。

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
		selfInterrupt();
}

tryAcquire(int arg)是AQS的抽象方法,具体功能由继承的子类去实现。在NonFairSync的实现如下:

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
	final Thread current = Thread.currentThread();
	int c = getState();
	if (c == 0) {
		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;
}

    state为0时设置当前线程独占锁。否则判断当前的线程是否时独占的线程(重入),如果是累加state值。如果上述都失败,则返回false。

    addWaiter函数完成的功能是将调用此方法的线程封装成为一个结点并放入Sync queue的对尾。(AQS维护了两个数据结构,双向链表Node,单向链表Condition-与Lock搭配使用)。

    放入都列后调用acquireQueued方法,如果当前节点的前驱节点是头节点且该节点能获取锁,则将当前节点设为头节点。否则判断节点的前驱节点的watiState是否等于SIGNAL,如果是就阻塞当前线程。整个acquireQueued方法是自旋的,会一直到线程获取到锁才退出。

    节点的几种状态值:

    CANCELLED,值为1,表示当前的线程被取消
    SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark
    CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中
    PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行

3.释放锁:AQS的release(int arg),tryRelease(int arg)由子类实现

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
	}
    return false;
}

trRelease(int arg)源码:

protected final boolean tryRelease(int releases) {
	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;
}

    1).判断如果释放锁当前线程不是独占的线程,就抛出异常。

    2).如果计算后线程的state=0,则返回true。

    3).线程释放锁后,如果头节点部位空且waitStatus != 0 则唤醒该节点的下一个节点(已在请求锁时被阻塞)。

4.公平锁的释放锁步骤与非公平锁一直,只是在获取锁的时候不同:

	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 final boolean hasQueuedPredecessors() {
	// The correctness of this depends on head being initialized
	// before tail and on head.next being accurate if the current
	// thread is first in queue.
	Node t = tail; // Read fields in reverse initialization order
	Node h = head;
	Node s;
	return h != t &&
		((s = h.next) == null || s.thread != Thread.currentThread());
}

    hasQueuedPredecessors()的作用是判断如果当前线程是第一个请求锁的线程则返回false,后续操作去获取锁。否则返回true,让当前线程去排队,这就是公平锁的原理。

5.Reentrant与Synchronized相比:

1).与synchronized相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间   锁等候,可中断锁等候,锁投票。
2).ReentrantLock还提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock更加适合.
3).ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些。
4).ReentrantLock支持更加灵活的同步代码块,但是使用synchronized时,只能在同一个synchronized块结构中获取和释放。注:ReentrantLock的锁释放一定要在finally中处理,否则可能会产生严重的后果。
5).ReentrantLock支持中断处理,且性能较synchronized会好些。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值