看本文建议结合源码
首先来看看ReentrantLock的构造方法,它的构造方法有两个,如所示:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(booleanfair) {
sync = fair ? new FairSync() : new NonfairSync();
}
在ReentrantLock中定义了三个内部类,分别是Sync以及Sync的子类FairSync、NonfairSync,分别实现了公平策略和非公平策略,默认实现的是非公平策略。
公平和非公平的第一个区分点,首先来看NonfairSync的lock方法
finalvoid lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
假设有三个并发的线程,第一个线程调用lock的时候,compareAndSetState(0,1)是可以正常执行的返回true,然后把当前线程置为独占模式的当前所有者,然后该线程可以执行后续操作。如果第二个线程被放到队列中,第三个线程走到compareAndSetState(0,1),第一个线程刚好unlock掉,那么第三个线程就会直接执行,而不是等待第二个线程执行后,这也就是非公平模式的特点。第二个线程调用lock的时候,compareAndSetState(0,1)返回false,执行acquire(1),该方法会调用tryAcquire(1)和acquireQueued(addWaiter(Node.EXCLUSIVE), 1)
先来看下tryAcquire(1),该方法的实现是nonfairTryAcquire方法,代码如下:
finalboolean nonfairTryAcquire(intacquires) {
final Thread current = Thread.currentThread();
intc = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
returntrue;
}
}
elseif (current == getExclusiveOwnerThread()) {
intnextc = c + acquires;
if (nextc < 0) // overflow
thrownew Error("Maximum lock count exceeded");
setState(nextc);
returntrue;
}
returnfalse;
}
看代码,结合三个线程并发的场景,第一个线程lock后,会把state(默认值是0)的值置为1,所以c的值现在是1,getExclusiveOwnerThread()是第一个线程,所以直接返回false,执行acquireQueued(addWaiter(Node.EXCLUSIVE), 1),方法addWaiter(Node.EXCLUSIVE)的作用是根据给的排他模式把当前线程(也就是第二个线程)加入队列中,此时的队列情况如下:
首次有线程(Node)进入队列时,会先new一个Node对象作为头部,然后把首次进队列的线程(Node)挂在其后面。Node对象是包含当前线程和模型的作为队列元素
然后执行acquireQueued(final Node node, int arg),来看下代码:
finalboolean acquireQueued(final Node node, intarg) {
booleanfailed = true;
try {
booleaninterrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
returninterrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
第二个线程入队列,前面是一个初始化的对象也是head的值,进入循环后,会再次tryAcquire(1),假设依然获取不到,则进入第二个if语句中,执行shouldParkAfterFailedAcquire(p, node)方法和parkAndCheckInterrupt()方法,parkAndCheckInterrupt()就会把第二个线程阻塞起来。
公平和非公平的第二个区分点在tryAcquire(int acquires)方法中,如下:
protectedfinalboolean tryAcquire(intacquires) {
final Thread current = Thread.currentThread();
intc = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
returntrue;
}
}
elseif (current == getExclusiveOwnerThread()) {
intnextc = c + acquires;
if (nextc < 0)
thrownew Error("Maximum lock count exceeded");
setState(nextc);
returntrue;
}
returnfalse;
}
当第一个线程走完,第二个线程在队列中,第三个线程调用acquire(int arg)时先调用tryAcquire方法,会进行!hasQueuedPredecessors() && compareAndSetState(0, acquires)判断,而非公平的只有进行了compareAndSetState(0, acquires)判断,不考虑队列是否有等待的线程,而hasQueuedPredecessors()该方法就是判断队列中是否有阻塞的线程,如果有返回true,然后执行acquireQueued方法入队列。在第三个线程入队列同时,第一个线程走完调用unlock操作,该操作就会去调用release方法,如下
publicfinalboolean release(intarg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
returntrue;
}
returnfalse;
}
最终调用unparkSuccessor(h)方法,
privatevoid unparkSuccessor(Node node) {
intws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
从队列中获取第二个线程,unpark操作。阻塞结束后还在acquireQueued(final Node node, intarg)的循环体中,此时!hasQueuedPredecessors()和compareAndSetState(0, acquires)都为true,并把当前线程置为独占模式的当前所有者,然后该线程可以执行程序中lock()方法的后续操作。
相关推荐
Java并发之AQS详解 AbstractQueuedSynchronizer(AQS)是 Java 并发编程中的一个核心组件,提供了一套多线程访问共享资源的同步器框架。AQS 定义了两种资源共享方式:Exclusive(独占)和 Share(共享)。在 AQS 中...
本文深入探讨了Java并发编程的关键组件——抽象队列同步器(AQS)及其在ReentrantLock的应用。AQS是处理线程同步问题的高效工具,是Java并发编程中的核心。文章首先简要介绍了并发编程领域的先驱Doug Lea。重点在于...
通过对AQS和ReentrantLock的学习,我们了解到它们是Java并发编程中非常重要的组成部分。AQS作为底层框架,为多种同步器提供了统一的实现方案;而ReentrantLock则是在AQS基础上实现了高级锁的功能,使得开发者可以更...
AQS和`ReentrantLock`是Java并发编程中重要的组成部分,通过对它们的理解和掌握,可以更好地设计和实现高性能的并发程序。通过本文的学习,读者可以了解到这些核心概念和技术的实际应用,并能够根据具体的业务需求...
Java并发系列之ReentrantLock源码分析 ReentrantLock是Java 5.0中引入的一种新的加锁机制,它实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。ReentrantLock的底层实现是通过AQS来实现多线程同步...
AQS在Java并发编程中有广泛的应用,如ReentrantLock、CountDownLatch、Semaphore等。这些类都是通过继承AQS实现的,它们使用AQS提供的状态变量和队列来实现同步功能。 CountDownLatch的实现机制 CountDownLatch是...
Java并发编程是Java语言中最为复杂且重要的部分之一,它涉及了多线程编程、内存模型、同步机制等多个领域。为了深入理解Java并发编程,有必要了解其核心技术点和相关实现原理,以下将详细介绍文件中提及的关键知识点...
### JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS #### 引言 《JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS》是一篇详细介绍Java实用并发工具包(Java Util Concurrency,简称J.U.C.)中重要...
在Java并发编程中,理解和掌握并发锁的原理与实现至关重要,因为它们是解决多线程环境下的互斥和同步问题的关键。本文将基于JDK源码解析Java领域中的并发锁,探讨AQS基础同步器、LockSupport、Condition接口、Lock...
J.U.C提供了多种锁的实现,比如Lock接口及其实现ReentrantLock,提供了比synchronized更灵活的锁机制,同时AQS(AbstractQueuedSynchronizer)是实现各种锁的基础同步器,它管理着一系列的等待线程,并提供一种框架...
理解并熟练掌握JUC和AQS对于Java并发编程至关重要,它们提供了强大且灵活的工具,可以有效解决多线程环境下的同步和通信问题,提高程序的性能和可扩展性。通过自定义AQS的子类,开发者可以根据实际需求构建出满足...
Java并发编程是Java开发中必不可少的一部分,涉及到多线程、同步机制、线程池以及并发工具类等多个核心知识点。以下是对这些主题的详细说明: 1. **线程安全与锁 Synchronized 底层实现原理**: 线程安全是指在多...
04-Java并发线程池底层原理详解与源码分析-monkey 05-并发编程之深入理解Java线程-fox 06-并发编程之CAS&Atomic原子操作详解-fox 07-并发锁机制之深入理解synchronized(一)-fox 08-并发锁机制之深入理解...
Java 并发之 ReentrantLock ReentrantLock 是 Java 并发包中的一个技术,在并发编程中非常常用。它是一个实现 Lock 接口的类,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞...
### Java并发编程-AQS和JUC实战 #### 一、ReentrantLock 重入锁 **1.1 概述** - **基本介绍**: `ReentrantLock` 是一个实现了 `Lock` 接口的可重入互斥锁,提供比 `synchronized` 更丰富的功能。与 `synchronized...
在Java并发编程领域,AbstractQueuedSynchronizer(简称AQS)是一个核心组件,它是Java并发库中的基石,被许多并发工具类如ReentrantLock、Semaphore、CountDownLatch等作为基础框架来实现。AQS通过维护一个FIFO等待...
Java中的显示锁ReentrantLock使用与原理详解 Java中的显示锁ReentrantLock是Java concurrency API中的一种同步机制,用于解决多线程安全问题。ReentrantLock是Java 5中引入的,它是一种可重入锁,允许同一个线程多...
在Java并发编程中,如ReentrantLock、Semaphore和CountDownLatch等类的底层实现都离不开AQS。 **一、AQS概述** AQS 是一个抽象的队列同步器,它的核心是一个 volatile int 类型的 `state` 字段,表示资源的状态。...
Java并发之ASQ,即AbstractQueuedSynchronizer(AQS),是Java并发编程中一个至关重要的技术,它为构建锁和其他同步组件提供了基础框架。AQS是一个抽象类,通过继承来实现具体的同步组件。虽然AQS本身并不实现任何...