Java 并发锁ReentrantLock的实现
全路径名:java.util.concurrent.locks.ReentrantLock 类定义如下:
/**
* @since 1.5
*/
public class ReentrantLock implements Lock, java.io.Serializable {
...
}
ReentrantLock 类实现了 Lock 接口,JDK1.5 引入。
ReentrantLock 使用上分公平锁和非公平锁,两种锁机制。默认无参构造方法 ReentrantLock() 创建的是非公平锁。可以使用有参构造函数 ReentrantLock(boolean fair) 选择使用公平锁,还是非公平锁。具体实现是通过 ReentrantLock 的内部类 FairSync 和 NonfairSync 来实现的。FairSync 和 NonfairSync 是 ReentrantLock 类中抽象内部类 Sync 的子类。具体源码如下:
public class ReentrantLock implements Lock, java.io.Serializable {
...
private final Sync sync;
...
abstract static class Sync extends AbstractQueuedSynchronizer {...}
static final class NonfairSync extends Sync {...}
static final class FairSync extends Sync {...}
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
...
}java.util.concurrent.locks.Lock 接口定义了5个方法。具体源码如下:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
通过对 ReentrantLock 类 Lock 接口 lock() 方法的实现, 来看 ReentrantLock 是如何实现公平锁的。
先说方式,再看源码更好理解一点。如果要公平,那就要有先来后到。就像超市购物结账一样:
- 如果结账时,恰好没有人,那就直接结账。直接就拿到了锁。
- 如果结账时,已经有人了,那就排到队伍的后面,等到你的时候才可以结账。也就是拿到了锁。
ReentrantLock 内部类 FairSync 负责实现公平锁机制,FairSync 类继承了 Sync 类,Sync类 继承了 AbstractQueuedSynchronizer 类。下面是与 lock() 方法有个的源码:
static final class FairSync extends Sync {
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;
}
}
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
...
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
...
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
...
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node)
&& parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
}
FairSync 的 lock() 方法调用 AbstractQueuedSynchronizer 类 acquire() 方法获取锁。AbstractQueuedSynchronizer 类 acquire() 方法中,先使用 FairSync 类的 tryAcquire() 方法实现没有人排队的场景。
- int c = getState(): 获取的是 AbstractQueuedSynchronizer 类的一个状态。c = 0 代表目前没有线程拿到这个锁
- !hasQueuedPredecessors(): 没有其他线程排队
- compareAndSetState(0, acquires):使用 CAS(Compare and Swap)CPU 硬件同步原语机制获取锁
- setExclusiveOwnerThread(current):如果成功拿到锁,将当前线程和锁绑定
- else if (current == getExclusiveOwnerThread()) 如果锁已经被当前线程绑定,状态 c 加 1。这块就体现了 ReentrantLock 可重入的概念。同一个线程未释放锁的情况下可以重复拿到锁,每次状态 c 加1。
AbstractQueuedSynchronizer 类 acquire() 方法中,如果没人排队的情况下,未能成功抢到锁。那就进入了排队的场景:
- for (;;) :无限循环
- if (p == head && tryAcquire(arg)):只有排队首的才有资格竞争锁。p 是获得锁的线程
- parkAndCheckInterrupt() 这个方法里实现的未获得锁的线程阻塞。这里不讨论。
看完 ReentrantLock 公平锁的实现,非公平锁的实现就简单了。非公平锁是通过 ReentrantLock 内部类 实现的,源码如下:
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
abstract static class Sync extends AbstractQueuedSynchronizer {
...
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;
}
...
}从源码看一看出,不排队,直接调用 compareAndSetState(0, 1) 方法抢。这就是非公平锁。
到此这篇关于Java 并发锁ReentrantLock的实现的文章就介绍到这了,更多相关Java 并发锁ReentrantLock内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
零基础学Java:Java开发工具 Eclipse 安装过程创建第一个Java项目及Eclipse的一些基础使用技巧
这篇文章主要介绍了零基础学Java:Java开发工具 Eclipse 安装过程创建第一个Java项目及Eclipse的一些基础使用技巧,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-09-09
idea启动报错:Command line is too long问题
在使用IDEA时,若遇到"Commandlineistoolong"错误,通常是因为命令行长度超限,这是因为IDEA通过命令行或文件将classpath传递至JVM,操作系统对命令行长度有限制,解决方法是切换至动态类路径,通过修改项目的workspace.xml文件2024-09-09
基于RecyclerChart的KLine的绘制Scale详解
这篇文章主要为大家详细介绍了基于RecyclerChart实现KLine绘制Scale的相关资料,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下2023-03-03


最新评论