首页
学习
活动
专区
工具
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元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券
http://www.vxiaotou.com