ReentrantLock学习

  

  对于并发工作,你需要某种方式来防止两个任务访问相同的资源,至少在关键阶段不能出现这种冲突情况。防止这种冲突的方法就是当资源被一个任务使用时,在其上加锁。在前面的文章--synchronized学习中,我们学习了Java中内建的同步机制synchronized的基本用法,在本文中,我们来学习Java中另一种锁ReentrantLock。

 ReentrantLock介绍

  ReentrantLock,通常译为再入锁,是Java 5中新加入的锁实现,它与synchronized基本语义相同。再入锁通过代码直接调用lock()方法获取,代码书写更加灵活。与此同时,ReentrantLock提供了很多实用的方法,能够实现很多synchronized无法做到的细节控制,比如可以控制fairness,也就是公平性,或者利用定义条件等。但是,编码中也需要注意,必须要明确调用unlock()方法释放锁,不然就会一直持有该锁。

 

  我们先看一个简单例子体验一下:

public class ReLockTest {private Lock lock = new ReentrantLock();public int shareState;public void add() {shareState ++ ;}public void printState() {System.out.println("shareState-->" + shareState);
    }public static void main(String[] args) throws Exception{ReLockTest reLockTest = new ReLockTest();Thread t1 = new Thread(()->{for(int i = 0; i<10000 ; i++)reLockTest.add();});Thread t2 = new Thread(()->{for(int i = 0; i<10000 ; i++)reLockTest.add();});t1.start();t2.start();t1.join();t2.join();Thread.sleep(2000);reLockTest.printState();}
}

/** 输出结果
* shareState-->14012
*/

  我们看到输出结果小于20000,这就是线程不安全,我们加上同步之后再看结果:

 

public class ReLockTest {private Lock lock = new ReentrantLock();public int shareState;public void add() {lock.lock();try {shareState ++ ;}catch(Exception e) {e.printStackTrace();}finally {lock.unlock();}}public void printState() {System.out.println("shareState-->" + shareState);
    }public static void main(String[] args) throws Exception{ReLockTest reLockTest = new ReLockTest();Thread t1 = new Thread(()->{for(int i = 0; i<10000 ; i++)reLockTest.add();});Thread t2 = new Thread(()->{for(int i = 0; i<10000 ; i++)reLockTest.add();});t1.start();t2.start();t1.join();t2.join();Thread.sleep(2000);reLockTest.printState();}
}
/** 输出结果
* shareState-->20000
*/

  

  我们看到在将自增操作上锁之后,输出结果就达到预期了。这就是ReentrantLock的基本用法,为了保证锁释放,每个lock()动作都对应一个try-catch-finally,这可以说是一个惯用法:

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {// do something
} finally {lock.unlock();
}

ReentrantLock用法

  ReentrantLock相比synchronized,虽然所需的代码比synchronized关键字要多,但也是因为可以像普通对象一样使用,所以可以利用其提供的各种便利方法,进行精细的同步操作,甚至是synchronized难以表达的用例,如:

带超时的获取锁尝试

  通过tryLock(long timeout, TimeUnit unit)方法实现,这是一个具有超时参数的尝试申请锁的方法,阻塞时间不会超过给定的值;如果成功则返回true。

可以指定公平性

  在创建ReentrantLock对象时往构造器中传入true即指定创建公平锁,这里所谓的公平是指在竞争场景中,当公平性为真时,会倾向于将锁赋予等待时间最久的线程。公平性是减少线程“饥饿”(个别线程长期等待锁,但始终无法获取)情况的发生的一个办法。

  如果使用synchronized,我们根本无法进行公平性的选择,其永远是不公平的,这也是主流操作系统线程调度的选择。通用场景中,公平性未必有想象中的那么重要,Java默认的调度策略很少会导致“饥饿”发生。与此同时,若要保证公平性则会引入额外开销,自然会导致一定的吞吐量下将。所以,只有当程序确实有公平性需要的时候,才有必要指定它。

可以响应中断请求

  通过lockInterruptibly()获得锁,但是会不确定地发生阻塞。如果线程被中断,抛出一个InterruptedException异常。

可以创建条件变量,将复杂晦涩的同步操作转变为直观可控的对象行为

  如果说ReentrantLock是synchronized的替代选择,Conition则是将wait、notify、notifyAll等操作转化为相应的对象,将复杂而晦涩的同步操作转变为直观可控的对象行为。

  可以通过Condition上调用await()来挂起一个任务,当外部条件发生变化,你可以通过调用signal()来通知这个任务,从而唤醒一个任务,或者调用signAll()来唤醒所有在这个Condition上被其自身挂起的任务,这是Condition最常见的用法。一个典型的应用场景就是标准类库中的ArrayBlockingQueue,下面我们结合部分其源码来分析一下:

  首先,在构造函数中初始化lock,在从lock中创建两个条件变量Condition分别是notEmpty和notFull。

/** Main lock guarding all access */
final ReentrantLock lock;/** Condition for waiting takes */
private final Condition notEmpty;/** Condition for waiting puts */
private final Condition notFull;public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();this.items = new Object[capacity];lock = new ReentrantLock(fair);notEmpty = lock.newCondition();notFull =  lock.newCondition();
}

  然后在take方法中,判断和等待条件满足:

public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0)notEmpty.await();return dequeue();} finally {lock.unlock();}
}

  在take方法中,如果队列为空,ArrayBlockingQueue的本义是获取对象的线程会阻塞,等待入队的发生,而不是直接返回,代码中是通过调用Condition的await方法来实现的,而当有元素入队之后又是如何触发被阻塞的take操作的呢?我们看看enqueue:

 

private void enqueue(E x) {// assert lock.getHoldCount() == 1;// assert items[putIndex] == null;final Object[] items = this.items;items[putIndex] = x;if (++putIndex == items.length)putIndex = 0;count++;notEmpty.signal();  // 通知等待的线程
}

  最后一行代码中就是通知等待的线程,这行执行结束后,被take阻塞的线程就会继续执行了。

  通过signal/await的组合,完成了条件判断和通知等待线程,非常顺畅就完成了状态流转。signal和await成对调用非常重要,不然假设只有await动作,线程会一直等待直到被打断(interrupt)。

ReentrantLock与Synchronized

  使用synchronized关键字时,需要写的代码量更少,而ReentrantLock的使用往往和try-catch-finally一起配套使用,代码量增加了。

  从性能角度,synchronized早期的实现比较低效,对比ReentrantLock,大多数场景性能都相差较大。但是在Java 6中对其进行了非常多的改进(可以参考synchronized底层实现学习),在高竞争情况下,ReentrantLock仍然有一定优势。

  我们知道synchronized获取的锁是monitor对象,而ReentrantLock获取的锁是什么呢,是否也是同一把锁呢?下文中,我们会深入源码去探究ReentrantLock获取锁的实现细节。

转载于:https://www.cnblogs.com/volcano-liu/p/10262183.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/484331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021年,深度学习还有哪些未饱和、有潜力且处于上升期的研究方向?

来自&#xff1a;知乎 作者&#xff1a;谢凌曦、数据误码率、Zhifeng编辑&#xff1a;极市平台 在目前的深度学习领域&#xff0c;有些研究方向已经较为成熟&#xff0c;实现新突破的难度与过去相比显著提升。那么就目前来看&#xff0c;还有哪些方向处于朝阳阶段&#xff0c;相…

832. Flipping an Image

题目来源&#xff1a; 自我感觉难度/真实难度&#xff1a; 题意&#xff1a; 分析&#xff1a; 自己的代码&#xff1a; class Solution(object):def flipAndInvertImage(self, A):""":type A: List[List[int]]:rtype: List[List[int]]"""B[]row…

浙江发布数字化改革标准化体系建设方案,将于2025年底建成

来源&#xff1a;中新网近日&#xff0c;记者从浙江省市场监管局获悉&#xff0c;《浙江省数字化改革标准化体系建设方案(2021-2025年)》(以下简称《方案》)已正式印发&#xff0c;该《方案》为浙江省数字化改革标准化体系建设工作明确了总体框架、分阶段目标、重点任务和相关保…

MFC基于对话框的商场交易软件实现

void CHomeViewDlg::OnClickedBtGuke() {// TODO: 在此添加控件通知处理程序代码CUserLoginDlg dlg;//注册窗框口类对象User user;user.m_UserType _T("顾客");dlg.SetUser(user);dlg.DoModal();//进入注册窗口窗口 }void CHomeViewDlg::OnClickedBtShangjia() {// …

GPU的发展历程、未来趋势及研制实践

来源&#xff1a;文章转载自期刊《微纳电子与智能制造》&#xff0c;作者&#xff1a;熊庭刚摘要凭借GPU强大的计算能力&#xff0c;超级计算机在数据处理、物理模拟、天气预测、现代制药、基因测序、先进制造、人工智能、密码分析等方面都有着广泛的应用。在2020年的新冠肺炎疫…

Mysql Explain的简单使用

Mysql Explain 主要重要的字段有上面红色方框圈出来的那几个。 type: 连接类型&#xff0c;一个好的SQL语句至少要达到range级别&#xff0c;杜绝出现all级别。key: 使用到的索引名&#xff0c;如果没有选择索引&#xff0c;值是NULL。可以采取强制索引方式。key_len: 索引长度…

Java-绘图相关技术

//学了太多的基础&#xff0c;感觉一直没太大的进步&#xff0c;因此&#xff0c;我决定增加以下学习的难度&#xff0c;以此来增加自己的学习兴趣&#xff1b; /*** Java 绘图相关技术的学习*Graphics类相关技术知识&#xff1a;* 1.Graphics类是所有图像绘图的基类&#xf…

20210712未来智能实验室收录资料

整理&#xff1a;未来智能实验室20210712未来智能实验室收录资料特别推荐&#xff1a;置顶收录未来智能实验室在人民日报《学术前沿》发表的城市大脑最新综述研究论文和报告《城市大脑的起源、发展与未来趋势》&#xff0c;从城市大脑的起源和理论基础&#xff1b;城市大脑的定…

为什么操作dom会消耗性能

因为对DOM的修改为影响网页的用户界面&#xff0c;重绘页面是一项昂贵的操作。太多的JavaScript DOM操作会导致一系列的重绘操作&#xff0c;为了确保执行结果的准确性&#xff0c;所有的修改操作是按顺序同步执行的。我们称这个过程叫做回流&#xff08;reflow&#xff09;&am…

RISC-V生态未来的三种可能~

来源&#xff1a;嵌入式资讯精选因为x86 架构与 Arm 架构商业使用都要付费&#xff0c;因此&#xff0c;RISC-V开源具有很大优势。今天就来说说 RISC-V 生态的内容。关于RISC-VRISC&#xff1a;Reduced Instruction Set Computer&#xff0c;即精简指令集计算机。RISC-V是基于R…

java-图像的几何变换

/** Java 绘图相关技术的学习*Graphics类相关技术知识&#xff1a; 1.图像的几何变换图像翻转&#xff1a;使用另一个重载的drawImage(Image img,int x_1,int y_1,目标矩形的第一坐标int x_2,int y_2,目标矩形的第二坐标int x1,int y1,源矩形的第一坐标int x2,int y2,源矩形的第…

VS Code配置C/C++

VS Code 配置C/C  准备工作 Mingw-w64下载&#xff0c;在vscode中&#xff0c;点击帮助文档&#xff0c;进入如下界面。 在左侧选择CMingw-w64 onWindows,进入如下界面。可以参考文档进行配置。 点击文档中的mingw-w64链接进入如下界面。 点击黄色区域链接进入到里面…

人脑启发AI设计:让神经网络统一翻译语音和文本

来源&#xff1a;图灵人工智能作者&#xff1a;Glaciohound编辑&#xff1a;陈彩娴能够统一地理解语音和文本&#xff0c;是人类能够形成通用语言能力的重要原因。那么&#xff0c;人工智能是否也能如此&#xff1f;最近&#xff0c;来自字节跳动和UIUC的研究人员借鉴这一思路&…

32 bit 与 64 bit 程序(2)比较

32 bit 与 64 bit 程序&#xff08;2&#xff09;区别 由于操作系统内存分配的不同&#xff0c;导致软件开发过程中&#xff0c;需要编译不同版本的软件。 几个重要概念&#xff1a; &#xff08;1&#xff09;这里所说的的32位与64位程序&#xff0c;是指经过编译器编译后的可…

2021世界人工智能大会AI Debate:图神经网络是否是实现认知智能的关键?

来源&#xff1a;学术头条2021年7月10日&#xff0c;WAIC&#xff08;2021世界人工智能大会&#xff09;“图神经网络与认知智能前沿技术论坛”在上海世博中心成功举办。清华大学计算机科学与技术系教授李涓子&#xff0c;复旦大学计算机科学技术学院教授黄萱菁&#xff0c;清华…

Tensorflow深度学习应用(筑基篇)

筑基篇 #codinggbk1.张量 用于描述数据&#xff0c;可以理解为多维数组,包含张量的名字&#xff0c;阶数&#xff0c;形状数值类型. Tensor("Add:0", shape(m,n,k), dtypefloat32),表示节点名为Add,0代表第0个输出&#xff0c;shape表示为MxNxK维的数组&…

WPF中自动增加行(动画)的TextBox

WPF中自动增加行&#xff08;动画&#xff09;的TextBox 原文:WPF中自动增加行&#xff08;动画&#xff09;的TextBoxWPF中自动增加行&#xff08;动画&#xff09;的TextBox WPF中的Textbox控件是可以自动换行的&#xff0c;只要设置TextWrapping属性为”Wrap”即可&#xff…

基因婴儿事未了!人类将在2031年开始实现永生?

来源:WPR前段时间&#xff0c;中国首例“基因编辑”婴儿事件闹得沸沸扬扬&#xff0c;这件事也发酵到了国外&#xff0c;不到一天&#xff0c;各路外媒全部刷屏了。在这些外媒报道里&#xff0c;最有意思的要数《纽约时报》杂志了。他们就“基因编辑”问题&#xff0c;请到了作…