什么是可重入锁?

在Java 5.0中,增加了一个新功能以增强内部锁定功能,称为可重入锁定。 在此之前,“同步”和“易失性”是实现并发的手段。

public synchronized void doAtomicTransfer(){
     //enter synchronized block , acquire lock over this object.
    operation1()
    operation2();    
} // exiting synchronized block, release lock over this object.

同步使用内部锁或监视器。 Java中的每个对象都有一个与之关联的固有锁。 每当线程尝试访问同步的块或方法时,它都会获取该对象的固有锁定或监视器。 在使用静态方法的情况下,线程获取对类对象的锁定。

就代码编写而言,内在锁定机制是一种干净的方法,对于大多数用例而言,它是非常好的。 那么,为什么我们需要显式锁的其他功能? 来!我们讨论一下。

内部锁定机制可能具有一些功能限制,例如:

  1. 无法中断等待获取锁的线程。 (间断锁定)
  2. 如果不想永远等待,就不可能尝试获取锁。 (尝试锁定)
  3. 无法实现非块结构的锁定规则,因为必须在获取它们的同一块中释放固有锁。

除此之外,ReentrantLock还支持锁轮询和支持超时的可中断锁等待。 ReentrantLock还支持可配置的公平性策略,从而允许更灵活的线程调度。

让我们看一下ReentrantLock类(实现Lock)实现的几种方法:

void lock();
 void lockInterruptibly() throws InterruptedException;
 boolean tryLock();
 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
.....

让我们尝试了解这些用法,看看我们可以获得什么好处。

  1. 轮询和定时锁获取:

    让我们看一下示例代码:

    public void transferMoneyWithSync(Account fromAccount, Account toAccount,
    			float amount) throws InsufficientAmountException {
    
    		synchronized (fromAccount) {
    			// acquired lock on fromAccount Object
    			synchronized (toAccount) {
    				// acquired lock on toAccount Object
    				if (amount > fromAccount.getCurrentAmount()) {
    					throw new InsufficientAmountException(
    							"Insufficient Balance");
    				} else {
    					fromAccount.debit(amount);
    					toAccount.credit(amount);
    				}
    			}
    		}
    	}

    在上面的transferMoney()方法中,当2个线程A和B几乎同时尝试转移资金时,可能会出现死锁。

    A: transferMoney(acc1, acc2, 20);
    B: transferMoney(acc2, acc1 ,25);

    线程A可能已获得对acc1对象的锁定,并正在等待获取对acc2对象的锁定,同时线程B已获得了对acc2对象的锁定,并且正在等待对acc1的锁定。 这将导致死锁,并且必须重新启动系统!!

    但是,有一种避免这种情况的方法,也就是所谓的“锁定排序”,我个人认为这有点复杂。

    ReentrantLock使用tryLock()方法实现了一种更干净的方法。 这种方法称为“定时轮询轮询获取”。 如果您无法获取所有必需的锁,释放已获取的锁并重试,它可以让您重新获得控制权。

    因此,使用tryLock我们将尝试获取两个锁,如果无法同时获取两个,则释放它们(如果已获取其中之一),然后重试。

    public boolean transferMoneyWithTryLock(Account fromAccount,
    			Account toAccount, float amount) throws InsufficientAmountException, InterruptedException {
    
    		// we are defining a stopTime
    		long stopTime = System.nanoTime() + 5000;
    		while (true) {
    			if (fromAccount.lock.tryLock()) {
    				try {
    					if (toAccount.lock.tryLock()) {
    						try {
    							if (amount > fromAccount.getCurrentAmount()) {
    								throw new InsufficientAmountException(
    										"Insufficient Balance");
    							} else {
    								fromAccount.debit(amount);
    								toAccount.credit(amount);
    							}
    
    						} finally {
    							toAccount.lock.unlock();
    						}
    					}
    
    				} finally {
    					fromAccount.lock.unlock();
    				}
    			}
    			if(System.nanoTime() < stopTime)
    				return false;
    
    			Thread.sleep(100);
    		}//while
    	}

    在这里,我们实现了定时锁,因此,如果在指定时间内无法获取锁,则transferMoney方法将返回失败通知并正常退出。

    我们还可以使用此概念来维护时间预算活动。

  2. 可中断锁获取:

    可中断的锁获取允许在可取消的活动中使用锁。

    lockInterruptible方法使我们能够尝试获取锁,但可用于中断。 所以基本上这意味着 它允许线程立即响应从另一个线程发送给它的中断信号。
    当我们想向所有等待的锁发送KILL信号时,这将很有帮助。

    让我们看一个例子,假设我们有一条共享的线来发送消息,我们希望以这样的方式设计它:如果另一个线程来了并中断了当前线程,则该线程应释放锁并执行退出或关闭操作以取消当前任务。

    public boolean sendOnSharedLine(String message) throws InterruptedException{
    		lock.lockInterruptibly();
    		try{
    			return cancellableSendOnSharedLine(message);
    		} finally {
    			lock.unlock();
    		}
    	}
    
    private boolean cancellableSendOnSharedLine(String message){
    .......

    定时tryLock也可响应中断。

  3. 非块结构锁定:

    在固有锁中,获取-释放对是块结构的,即,无论控制如何退出该锁,始终在获取该锁的同一基本块中释放该锁。

    外在锁提供了进行更明确控制的功能。
    使用外部锁可以更轻松地实现某些概念,例如“锁紧皮带”。 在哈希混和集合和链接列表中可以看到一些用例。

  4. 公平:

    ReentrantLock构造函数提供两种公平性选项供您选择:创建非公平锁或公平锁。 公平锁的线程只能在它们所要求的顺序获取锁,而一个不公平的锁允许锁获取它反过来的,这就是所谓的驳运 (打破了队列和获取锁,当它变得可用)。

    由于挂起和恢复线程的开销,公平锁定会带来巨大的性能成本。 在某些情况下,恢复挂起的线程与实际运行之间会有明显的延迟。 让我们看一下情况:

    A -> holds lock
    B -> has requested and is in suspended state waiting for A to release lock
    C -> requests the lock at the same time when A releases the lock, C has not yet gone to suspended state.

    由于C尚未处于挂起状态,因此它有可能获得A释放的锁,使用它并在B甚至还没有唤醒之前释放它。 因此,在这种情况下,不公平锁定具有明显的性能优势。

    内部锁和外部锁在内部具有相同的锁定机制,因此性能的提高纯粹是主观的。 这取决于我们上面讨论的用例。 外部锁提供了更明确的控制机制,可以更好地处理死锁,饥饿等。我们将在以后的博客中看到更多示例。

参考: 什么是可重入锁? 从我们的JCG合作伙伴 Anirudh Bhatnagar在anirudh bhatnagar博客上获得。

翻译自: https://www.javacodegeeks.com/2013/11/what-are-reentrant-locks.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值