首页 > 其他分享 >ReentrantLock可重入锁的使用场景及API的使用场景

ReentrantLock可重入锁的使用场景及API的使用场景

时间:2023-01-03 21:39:29浏览次数:49  
标签:场景 Thread lock ReentrantLock 该锁 API 线程 new

摘要

从使用场景的角度出发来介绍对ReentrantLock的使用,相对来说容易理解一些。

场景1:如果发现该操作已经在执行中则不再执行(有状态执行)

a、用在定时任务时,如果任务执行时间可能超过下次计划执行时间,确保该有状态任务只有一个正在执行,忽略重复触发。
b、用在界面交互时点击执行较长时间请求操作时,防止多次点击导致后台重复执行(忽略重复触发)。

以上两种情况多用于进行非重要任务防止重复执行,(如:清除无用临时文件,检查某些资源的可用性,数据备份操作等)

private ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) { //如果已经被lock,则立即返回false不会等待,达到忽略操作的效果

try {

//操作

} finally {
lock.unlock();
}

}

场景2:如果发现该操作已经在执行,等待一个一个执行(同步执行,类似synchronized)

这种比较常见大家也都在用,主要是防止资源使用冲突,保证同一时间内只有一个操作可以使用该资源。
但与synchronized的明显区别是性能优势(伴随jvm的优化这个差距在减小)。同时Lock有更灵活的锁定方式,公平锁与不公平锁,而synchronized永远是公平的。

这种情况主要用于对资源的争抢(如:文件操作,同步消息发送,有状态的操作等)

ReentrantLock默认情况下为不公平锁

 

​​private​​​ ​​ReentrantLock lock = ​​​​new​​​ ​​ReentrantLock(); ​​​​//参数默认false,不公平锁​​
​​private​​​ ​​ReentrantLock lock = ​​​​new​​​ ​​ReentrantLock(​​​​true​​​​); ​​​​//公平锁​​


​​try​​​ ​​{​​
​​lock.lock(); ​​​​//如果被其它资源锁定,会在此等待锁释放,达到暂停的效果​​

​​//操作​​

​​} ​​​​finally​​​ ​​{​​
​​lock.unlock();​​
​​}​​

不公平锁与公平锁的区别:

公平情况下,操作会排一个队按顺序执行,来保证执行顺序。(会消耗更多的时间来排队)
不公平情况下,是无序状态允许插队,jvm会自动计算如何处理更快速来调度插队。(如果不关心顺序,这个速度会更快)

 

场景3:如果发现该操作已经在执行,则尝试等待一段时间,等待超时则不执行(尝试等待执行)

这种其实属于场景2的改进,等待获得锁的操作有一个时间的限制,如果超时则放弃执行。
用来防止由于资源处理不当长时间占用导致死锁情况(大家都在等待资源,导致线程队列溢出)。


​​try​​​ ​​{​​
​​if​​​ ​​(lock.tryLock(​​​​5​​​​, TimeUnit.SECONDS)) { ​​​​//如果已经被lock,尝试等待5s,看是否可以获得锁,如果5s后仍然无法获得锁则返回false继续执行​​


​​try​​​ ​​{​​


​​//操作​​


​​} ​​​​finally​​​ ​​{​​
​​lock.unlock();​​
​​}​​


​​}​​
​​} ​​​​catch​​​ ​​(InterruptedException e) {​​
​​e.printStackTrace(); ​​​​//当前线程被中断时(interrupt),会抛InterruptedException ​​
​​}​​



​​<div>​​




​​</div>​​
​​<span></span>​​

 

场景4:如果发现该操作已经在执行,等待执行。这时可中断正在进行的操作立刻释放锁继续下一操作。

synchronized与Lock在默认情况下是不会响应中断(interrupt)操作,会继续执行完。lockInterruptibly()提供了可中断锁来解决此问题。(场景2的另一种改进,没有超时,只能等待中断或执行完毕)

这种情况主要用于取消某些操作对资源的占用。如:(取消正在同步运行的操作,来防止不正常操作长时间占用造成的阻塞)

​​try​​​ ​​{​​
​​lock.lockInterruptibly();​​
​​//操作​​

​​} ​​​​catch​​​ ​​(InterruptedException e) {​​
​​e.printStackTrace();​​
​​} ​​​​finally​​​ ​​{​​
​​lock.unlock();​​
​​}​​


可重入概念

若一个程序或子程序可以“安全的被并行执行(Parallel computing)”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,可以再次进入并执行它(并行执行时,个别的执行结果,都符合设计时的预期)。可重入概念是在单线程操作系统的时代提出的。


 

Lock,tryLock,lockInterruptibly区别

先把API粘贴上来
lock
public void lock()
获取锁。
如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。
如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。
如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一
直处于休眠状态,此时锁保持计数被设置为 1。

指定者:
接口 Lock 中的 lock

 

lockInterruptibly
public void lockInterruptibly() throws InterruptedException
1)如果当前线程未被中断,则获取锁。
2)如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。
3)如果当前线程已经保持此锁,则将保持计数加 1,并且该方法立即返回。
4)如果锁被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以
前,该线程将一直处于休眠状态:
1)锁由当前线程获得;或者
2)其他某个线程中断当前线程。

5)如果当前线程获得该锁,则将锁保持计数设置为 1。
如果当前线程:
1)在进入此方法时已经设置了该线程的中断状态;或者
2)在等待获取锁的同时被中断。
则抛出 InterruptedException,并且清除当前线程的已中断状态。


6)在此实现中,因为此方法是一个显式中断点,所以要优先考虑响应中断,而不是响应锁的普通获取或
重入获取。

指定者: 接口 Lock 中的 lockInterruptibly
抛出: InterruptedException 如果当前线程已中断。

 

tryLock public boolean tryLock()

仅在调用时锁未被另一个线程保持的情况下,才获取该锁。

1)如果该锁没有被另一个线程保持,并且立即返回 true 值,则将锁的保持计数设置为 1。
即使已将此锁设置为使用公平排序策略,但是调用 tryLock() 仍将 立即获取锁(如果有可用的),
而不管其他线程当前是否正在等待该锁。在某些情况下,此“闯入”行为可能很有用,即使它会打破公
平性也如此。如果希望遵守此锁的公平设置,则使用 tryLock(0, TimeUnit.SECONDS)
,它几乎是等效的(也检测中断)。

2)如果当前线程已经保持此锁,则将保持计数加 1,该方法将返回 true。

3)如果锁被另一个线程保持,则此方法将立即返回 false 值。

指定者:
接口 Lock 中的 tryLock
返回:
如果锁是自由的并且被当前线程获取,或者当前线程已经保持该锁,则返回 true;否则返回
false

 

关于中断又是一段很长的叙述,先不谈。

1)lock(), 拿不到lock就不罢休,不然线程就一直block。 比较无赖的做法。
2)tryLock(),马上返回,拿到lock就返回true,不然返回false。 比较潇洒的做法。
带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。比较聪明的做法。
3)lockInterruptibly()就稍微难理解一些。

先说说线程的打扰机制,每个线程都有一个 打扰 标志。这里分两种情况,
1. 线程在sleep或wait,join, 此时如果别的进程调用此进程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;(thread在做IO操作时也可能有类似行为,见java thread api)
2. 此线程在运行中, 则不会收到提醒。但是 此线程的 “打扰标志”会被设置, 可以通过isInterrupted()查看并 作出处理。
lockInterruptibly()和上面的第一种情况是一样的, 线程在请求lock并被阻塞时,如果被interrupt,则“此线程会被唤醒并被要求处理InterruptedException”。并且如果线程已经被interrupt,再使用lockInterruptibly的时候,此线程也会被要求处理interruptedException

先看lock()方法

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author 作者 E-mail:
* @version 创建时间:2015-10-23 下午01:47:03 类说明
*/
public class TestLock
{
// @Test
public void test() throws Exception
{
final Lock lock = new ReentrantLock();
lock.lock();


Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
lock.lock();
System.out.println(Thread.currentThread().getName() + " interrupted.");
}
},"child thread -1");

t1.start();
Thread.sleep(1000);

t1.interrupt();

Thread.sleep(1000000);
}

public static void main(String[] args) throws Exception
{
new TestLock().test();
}
}

用eclipse对这个程序进行debug发现,即使子线程已经被打断,但是子线程仍然在run,可见lock()方法并不关心线程是否被打断,甚至说主线程已经运行完毕,子线程仍然在block().

ReentrantLock可重入锁的使用场景及API的使用场景_公平锁

 

而使用LockInterupptibly,则会响应中断

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* @author 作者 E-mail:
* @version 创建时间:2015-10-23 下午01:53:10 类说明
*/
public class TestLockInterruptibly
{

// @Test
public void test3() throws Exception
{
final Lock lock = new ReentrantLock();
lock.lock();

Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
lock.lockInterruptibly();
}
catch(InterruptedException e)
{
System.out.println(Thread.currentThread().getName() + " interrupted.");
}
}
}, "child thread -1");

t1.start();
Thread.sleep(1000);

t1.interrupt();

Thread.sleep(1000000);
}

public static void main(String[] args) throws Exception
{
new TestLockInterruptibly().test3();
}
}

ReentrantLock可重入锁的使用场景及API的使用场景_ReentrantLock_02

 

try{
Thread.sleep(2000);
lock.lockInterruptibly();
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+" interrupted.");
}

 

t1.start();
t1.interrupt();
Thread.sleep(1000000);

如果将代码改成这样,那么将会在在阻塞之前已经中断,此时再lockInterruptibly()也是会相应中断异常的

标签:场景,Thread,lock,ReentrantLock,该锁,API,线程,new
From: https://blog.51cto.com/u_15147537/5986776

相关文章

  • 场景题:假设10W人突访,你的系统如何做到不 雪崩?
    文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面......
  • 华为云CDN,多场景网络加速服务,为企业发展强势赋能
    ​很多网友认为,只要家里的宽度速度足够快,电脑或者手机性能足够强,那么上网玩游戏或者下载文件就可以非常流畅快速,其实不然,相信大家都遇到过明明网络没问题但是下载视频或者文......
  • part1_Keras的API及架构
    1Keras概述1.1为什么选择KerasKeras是用于构造神经网络模型的API,Keras由纯Python编写而成,并基于Tensorflow、Theano和CNTK后端,因此Keras是一个移植性较强的框架。Keras......
  • Restful API规范
    定义restful,全称为RepresentationalStateTransfer,翻译为中文叫做资源状态转换(表征性状态转移)。Restful风格是一种专门为Web开发而定义API接口的设计风格,尤其适用于前......
  • .net core WebApi 返回类型
         ......
  • 玩转OpenHarmony社交场景:即时通讯平台
    一、简介本样例是基于即时通讯(Instantmessaging,简称IM)服务实现的OpenAtomOpenHarmony(简称“OpenHarmony”)应用,允许两人或多人使用互联网即时地传递文字、图片、文件、语音......
  • vue3.0的全局api变化
    1.全局api使用的变化:vue3已经去除Vue语法,取代的是用createApp创建的app  2.其他改变2.1data函数的变化,在vue3data必须是一个函数,否则报错     2.2过......
  • 《迷你世界》亿级玩家都在用的游戏场景推荐系统长啥样?
    摘要:通过使用华为云企业级KV数据库GaussDB(forRedis),《迷你世界》的推荐业务不仅成本降低了60%,而且提升了画像数据承载量,让玩家更容易、更快速找到自己喜欢的游戏场景。......
  • SEO:在必应(bing)站长后台哪里获取到必应API?
    网站有时候需要用到主动推送API,关于必应的API需要在必应的后台获取,步骤如下:1.登录必应后台,点击“设置”>>>点击API访问  2.点击查看API密钥;  3.点击复制,即可获......
  • 03.基础框架场景和资源加载模块
    ​​​https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html​​usingSystem.Collections;usingSystem.Collections.Generic;usi......