首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Java并发基础:深度解析Reentrant可重入性实现

Java并发基础:深度解析Reentrant可重入性实现 - 程序员古德内容摘要

可重入锁有助于避免死锁,因为它允许线程在不释放已持有的锁的情况下,重新进入同步代码块,在某些情况下是非常必要的,

什么是可重入性?

锁的可重入性(Reentrant)是指同一个线程可以多次获取同一个锁,而不会导致死锁或其他线程无法获取该锁的情况,可重入锁是一种特殊的锁,它允许一个线程在已经持有该锁的情况下,再次获取(或重入)该锁,而不会产生冲突或死锁。

这种机制是通过为每个锁关联一个持有者和一个计数器来实现的,当线程首次获取锁时,它成为锁的持有者,并且计数器设置为1。如果同一个线程再次获取该锁,计数器就会增加,每次线程释放锁时,计数器都会减少,只有当计数器归零时,其他线程才有机会获取该锁。

可重入锁有助于避免死锁,因为它允许线程在不释放已持有的锁的情况下,重新进入同步代码块,在某些情况下是非常必要的,例如,在递归函数中,或者在需要调用其他也使用相同锁的方法时。

Java中的ReentrantLock类就是一个可重入锁的实现,此外,synchronized关键字在Java中提供的内置锁也是可重入的。

代码案例

下面是一个简单的代码示例,演示了ReentrantLock可重入锁的特性。

这个示例包括一个Counter类,它使用ReentrantLock来保护对内部计数器的访问,以及一个客户端类Client,它调用Counter的方法来增加计数器的值,如下代码:

import java.util.concurrent.locks.ReentrantLock;

/**

* @创建人 程序员古德

* @创建时间 2024/1/18 23:51

* @修改人 暂无

* @修改时间 暂无

* @版本历史 暂无

*/

// Counter类使用ReentrantLock来保护计数器的状态

public class Counter {

private final ReentrantLock lock = new ReentrantLock();

private int count = 0;

// 增加计数器的值

public void increment() {

lock.lock(); // 获取锁

try {

count++;

System.out.println("Counter incremented by " + Thread.currentThread().getName() + " to " + count);

} finally {

lock.unlock(); // 释放锁

}

}

// 递归增加计数器的值,演示可重入锁的特性

public void recursiveIncrement(int depth) {

if (depth <= 0) {

return;

}

lock.lock(); // 在递归调用中重复获取锁

try {

count++;

System.out.println("Counter recursively incremented by " + Thread.currentThread().getName() + " to " + count + " at depth " + depth);

recursiveIncrement(depth - 1); // 递归调用

} finally {

lock.unlock(); // 递归返回时释放锁

}

}

}

// 客户端类,用于调用Counter的方法

public class Client implements Runnable {

private final Counter counter;

public Client(Counter counter) {

this.counter = counter;

}

@Override

public void run() {

// 调用increment方法

counter.increment();

// 调用recursiveIncrement方法进行递归增加

counter.recursiveIncrement(3);

}

public static void main(String[] args) {

Counter counter = new Counter();

// 创建并启动两个客户端线程

Thread clientThread1 = new Thread(new Client(counter));

Thread clientThread2 = new Thread(new Client(counter));

clientThread1.start();

clientThread2.start();

// 注意:由于线程调度的不确定性,输出的顺序可能会有所不同

}

}

在上面代码中,Counter类有一个受ReentrantLock保护的count变量,increment方法简单地增加计数器的值,而recursiveIncrement方法递归地增加计数器的值,演示了同一个线程可以多次获取同一个锁而不会导致死锁的情况。程序运行结果如下:

Counter incremented by Thread-0 to 1

Counter recursively incremented by Thread-0 to 2 at depth 3

Counter recursively incremented by Thread-0 to 3 at depth 2

Counter recursively incremented by Thread-0 to 4 at depth 1

Counter incremented by Thread-1 to 5

Counter recursively incremented by Thread-1 to 6 at depth 3

Counter recursively incremented by Thread-1 to 7 at depth 2

Counter recursively incremented by Thread-1 to 8 at depth 1

这个输出显示了两个线程交替增加计数器的值,并且每个线程都能够递归地获取锁来增加计数器的值,而不会相互阻塞或导致死锁,这就是ReentrantLock可重入性的体现。

实现原理

Java并发基础:深度解析Reentrant可重入性实现 - 程序员古德

ReentrantLock的可重入性是通过其内部类Sync实现的,该类继承自AbstractQueuedSynchronizer(AQS),Sync有两个子类:NonfairSync和FairSync,分别表示非公平锁和公平锁,但它们在可重入性的实现上是相同的,可重入性的关键在于,当一个线程尝试获取锁时,如果锁已经被同一个线程持有,那么该线程可以再次获取锁而不会阻塞,这是通过在AQS中维护一个状态变量state和一个表示当前锁持有者的线程变量来实现的。

以下是ReentrantLock中与可重入性相关的关键代码段及其解释:

1、AQS的状态变量state:在AQS中,state变量用于表示同步状态,对于ReentrantLock,这个变量表示当前线程持有锁的重入次数。

2、Sync.tryAcquire(int acquires)方法:当线程尝试获取锁时,会调用此方法,它首先检查锁是否已经被当前线程持有,如果是,则增加重入计数,如果不是,则尝试获取锁。

如下代码案例:

protected final boolean tryAcquire(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,表示锁未被持有,尝试通过CAS操作将其设置为acquires(通常为1),并将当前线程设置为锁的独占所有者。

如果当前线程已经是锁的独占所有者,则增加state的值,表示重入次数增加。

如果其他线程尝试获取锁,则返回false。

3、Sync.tryRelease(int releases)方法:当线程释放锁时,会调用此方法,它减少重入计数,并在计数为0时释放锁,如下代码:

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;

}

代码解释:

从state中减去releases(通常为1),表示减少重入次数。

检查当前线程是否是锁的独占所有者,如果不是,则抛出异常。

如果重入次数减至0,将锁的独占所有者设置为null,并标记free为true表示锁已被完全释放。

更新state的值。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OYmGGvd2_11p705y-vrNe1yw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券