Java并发教程–锁定:显式锁定

1.简介

在许多情况下,使用隐式锁定就足够了。 有时,我们将需要更复杂的功能。 在这种情况下, java.util.concurrent.locks包为我们提供了锁定对象。 当涉及到内存同步时,这些锁的内部机制与隐式锁相同。 区别在于显式锁提供了其他功能。

与隐式同步相比,主要优点或改进是:

  • 通过读取或写入来分离锁。
  • 一些锁允许并发访问共享资源( ReadWriteLock )。
  • 获取锁的不同方式:
    • 阻塞:lock()

2.锁对象的分类

锁定对象实现以下两个接口之一:

  • Lock :定义锁对象必须实现的基本功能。 基本上,这意味着获取和释放锁。 与隐式锁相反,此锁允许以非阻塞或可中断的方式(除阻塞方式外)获取锁。 主要实现:
    • 重入锁
  • ReadWriteLock :它保留一对锁,一个锁用于只读操作,另一个锁用于写操作。 读锁可以由不同的读取器线程同时获取(只要写锁尚未获取资源),而写锁是互斥的。 这样,只要没有写操作,我们就可以让多个线程同时读取资源。 主要实现:
    • 重入ReadWriteLock

下面的类图显示了不同锁类之间的关系:

显式锁定

3.重入锁

此锁的工作方式与同步块相同。 只要一个线程尚未被另一个线程获取,它就会获取该锁,并且直到调用unlock之前,它才会释放该锁。 如果另一个线程已经获取了该锁,则尝试获取该锁的线程将被阻塞,直到另一个线程释放它为止。

我们将从一个没有锁的简单示例开始,然后我们将添加一个可重入锁以查看其工作方式。

public class NoLocking {public static void main(String[] args) {Worker worker = new Worker();Thread t1 = new Thread(worker, "Thread-1");Thread t2 = new Thread(worker, "Thread-2");t1.start();t2.start();}private static class Worker implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " - 1");System.out.println(Thread.currentThread().getName() + " - 2");System.out.println(Thread.currentThread().getName() + " - 3");}}
}

由于上面的代码未同步,因此线程将被交错。 让我们看一下输出:

Thread-2 - 1
Thread-1 - 1
Thread-1 - 2
Thread-1 - 3
Thread-2 - 2
Thread-2 - 3

现在,我们将添加一个可重入锁,以序列化对run方法的访问:

public class ReentrantLockExample {public static void main(String[] args) {Worker worker = new Worker();Thread t1 = new Thread(worker, "Thread-1");Thread t2 = new Thread(worker, "Thread-2");t1.start();t2.start();}private static class Worker implements Runnable {private final ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {lock.lock();try {System.out.println(Thread.currentThread().getName() + " - 1");System.out.println(Thread.currentThread().getName() + " - 2");System.out.println(Thread.currentThread().getName() + " - 3");} finally {lock.unlock();}}}
}

上面的代码将安全地执行,而不会交错线程。 您可能意识到我们可以使用同步块,并且效果是相同的。 现在出现的问题是可重入锁提供给我们什么好处?

下面介绍了使用这种类型的锁的主要优点:

  • 通过实现Lock接口提供了获取锁的其他方式:
    • lockInterruptible :如果另一个线程拥有锁,则当前线程将尝试获取解除锁定并被阻塞,例如使用lock()方法。
  • ReentrantLock类提供的其他方法,主要用于监视或测试。 例如, getHoldCountisHeldByCurrentThread方法。

让我们看一个使用tryLock的示例,然后再继续下一个锁类。

3.1尝试获取锁

在下面的示例中,我们有两个线程,试图获取相同的两个锁。

一个线程获取lock2 ,然后阻止尝试获取lock1

public void lockBlocking() {LOGGER.info("{}|Trying to acquire lock2...", Thread.currentThread().getName());lock2.lock();try {LOGGER.info("{}|Lock2 acquired. Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock1.unlock();lock2.unlock();}
}

另一个线程获取lock1 ,然后尝试获取lock2

public void lockWithTry() {LOGGER.info("{}|Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();try {LOGGER.info("{}|Lock1 acquired. Trying to acquire lock2...", Thread.currentThread().getName());boolean acquired = lock2.tryLock(4, TimeUnit.SECONDS);if (acquired) {try {LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock2.unlock();}}else {LOGGER.info("{}|Failed acquiring lock2. Releasing lock1", Thread.currentThread().getName());}} catch (InterruptedException e) {//handle interrupted exception} finally {lock1.unlock();}
}

使用标准锁定方法,这将导致死锁,因为每个线程将永远等待另一个线程释放该锁。 但是,这次我们尝试使用tryLock指定超时来获取它。 如果四秒钟后仍未成功,它将取消操作并释放第一把锁。 这将允许另一个线程解锁并获得两个锁。

让我们看完整的例子:

public class TryLock {private static final Logger LOGGER = LoggerFactory.getLogger(TryLock.class);private final ReentrantLock lock1 = new ReentrantLock();private final ReentrantLock lock2 = new ReentrantLock();public static void main(String[] args) {TryLock app = new TryLock();Thread t1 = new Thread(new Worker1(app), "Thread-1");Thread t2 = new Thread(new Worker2(app), "Thread-2");t1.start();t2.start();}public void lockWithTry() {LOGGER.info("{}|Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();try {LOGGER.info("{}|Lock1 acquired. Trying to acquire lock2...", Thread.currentThread().getName());boolean acquired = lock2.tryLock(4, TimeUnit.SECONDS);if (acquired) {try {LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock2.unlock();}}else {LOGGER.info("{}|Failed acquiring lock2. Releasing lock1", Thread.currentThread().getName());}} catch (InterruptedException e) {//handle interrupted exception} finally {lock1.unlock();}}public void lockBlocking() {LOGGER.info("{}|Trying to acquire lock2...", Thread.currentThread().getName());lock2.lock();try {LOGGER.info("{}|Lock2 acquired. Trying to acquire lock1...", Thread.currentThread().getName());lock1.lock();LOGGER.info("{}|Both locks acquired", Thread.currentThread().getName());} finally {lock1.unlock();lock2.unlock();}}private static class Worker1 implements Runnable {private final TryLock app;public Worker1(TryLock app) {this.app = app;}@Overridepublic void run() {app.lockWithTry();}}private static class Worker2 implements Runnable {private final TryLock app;public Worker2(TryLock app) {this.app = app;}@Overridepublic void run() {app.lockBlocking();}}
}

如果执行代码,将产生以下输出:

13:06:38,654|Thread-2|Trying to acquire lock2...
13:06:38,654|Thread-1|Trying to acquire lock1...
13:06:38,655|Thread-2|Lock2 acquired. Trying to acquire lock1...
13:06:38,655|Thread-1|Lock1 acquired. Trying to acquire lock2...
13:06:42,658|Thread-1|Failed acquiring lock2. Releasing lock1
13:06:42,658|Thread-2|Both locks acquired

在第四行之后,每个线程都已获取一个锁,并且在尝试获取另一个锁时被阻塞。 在下一行,您会注意到四秒钟的间隔。 自超时以来,第一个线程无法获取锁并释放它已经获取的锁,从而允许第二个线程继续。

4. ReentrantReadWriteLock

这种类型的锁保留一对内部锁( ReadLockWriteLock )。 如接口所述,此锁允许多个线程同时从资源读取。 当资源具有频繁读取但很少写入的资源时,这特别方便。 只要没有需要写的线程,资源就将被并发访问。

以下示例显示了三个线程同时从共享资源读取。 当第四个线程需要写入时,它将排他地锁定资源,从而防止读取线程在写入时访问该资源。 一旦写入完成并释放了锁定,所有读取器线程将继续并发访问资源:

public class ReadWriteLockExample {private static final Logger LOGGER = LoggerFactory.getLogger(ReadWriteLockExample.class);final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private Data data = new Data("default value");public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();example.start();}private void start() {ExecutorService service = Executors.newFixedThreadPool(4);for (int i=0; i<3; i++) service.execute(new ReadWorker());service.execute(new WriteWorker());service.shutdown();}class ReadWorker implements Runnable {@Overridepublic void run() {for (int i = 0; i < 2; i++) {readWriteLock.readLock().lock();try {LOGGER.info("{}|Read lock acquired", Thread.currentThread().getName());Thread.sleep(3000);LOGGER.info("{}|Reading data: {}", Thread.currentThread().getName(), data.getValue());} catch (InterruptedException e) {//handle interrupted} finally {readWriteLock.readLock().unlock();}}}}class WriteWorker implements Runnable {@Overridepublic void run() {readWriteLock.writeLock().lock();try {LOGGER.info("{}|Write lock acquired", Thread.currentThread().getName());Thread.sleep(3000);data.setValue("changed value");LOGGER.info("{}|Writing data: changed value", Thread.currentThread().getName());} catch (InterruptedException e) {//handle interrupted} finally {readWriteLock.writeLock().unlock();}}}
}

控制台输出显示结果:

11:55:01,632|pool-1-thread-1|Read lock acquired
11:55:01,632|pool-1-thread-2|Read lock acquired
11:55:01,632|pool-1-thread-3|Read lock acquired
11:55:04,633|pool-1-thread-3|Reading data: default value
11:55:04,633|pool-1-thread-1|Reading data: default value
11:55:04,633|pool-1-thread-2|Reading data: default value
11:55:04,634|pool-1-thread-4|Write lock acquired
11:55:07,634|pool-1-thread-4|Writing data: changed value
11:55:07,634|pool-1-thread-3|Read lock acquired
11:55:07,635|pool-1-thread-1|Read lock acquired
11:55:07,635|pool-1-thread-2|Read lock acquired
11:55:10,636|pool-1-thread-3|Reading data: changed value
11:55:10,636|pool-1-thread-1|Reading data: changed value
11:55:10,636|pool-1-thread-2|Reading data: changed value

如您所见,当编写器线程获得写锁定(线程4)时,其他任何线程都无法访问该资源。

5.结论

这篇文章展示了显式锁的主要实现方式,并解释了与隐式锁有关的一些改进功能。 这篇文章是Java Concurrency Tutorial系列的一部分。 检查此处以阅读本教程的其余部分。

  • 您可以在Github上找到源代码。

翻译自: https://www.javacodegeeks.com/2015/02/java-concurrency-tutorial-locking-explicit-locks.html

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

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

相关文章

lda php,主题模型︱几款新主题模型——SentenceLDA、CopulaLDA、TWE简析与实现

[导读]百度最近开源了一个新的关于主题模型的项目。文档主题推断工具、语义匹配计算工具以及基于工业级语料训练的三种主题模型&#xff1a;LatentDirichlet Allocation(LDA)、SentenceLDA 和Topical Word Embedding(TWE)。一、Familia简介帮Familia&#xff0c;打个小广告~ F…

最短路径问题matlab作图,[MATLAB基础] matlab最短路径的输出问题,用了递归,但程序出错了,请大虾指点...

matlab最短路径的输出问题,用了递归,但程序出错了,请大虾指点 程序如下function shortestpath(w,p,q)c->;l;f;tsize(w);mt(1,1);azeros(m,m);vw;for k1:mfor i1:mfor j1:mb[w(i,j),w(i,k)w(k,j)];w(i,j)min(b);if w(i,j)~v(i,j)a(i,j)k;%a(i,j)10*a(i,j)k;vw;endendendendw;…

快速浏览JAX-RS请求与方法匹配

在本文中&#xff0c;我们来看一下JAX-RS中与资源方法匹配的HTTP请求 。 它是JAX-RS的最基本功能之一。 通常&#xff0c;使用JAX-RS API的开发人员不会接触&#xff08;或真正不需要知道&#xff09; 匹配过程的细节&#xff0c;请放心&#xff0c;由于我们的RESTful&#xff…

$stat php,php stat函数怎么用

php stat函数用于返回关于文件的信息&#xff0c;其语法是fstat(file)&#xff0c;参数file必需&#xff0c;指规定要检查的文件。php stat函数怎么用&#xff1f;定义和用法stat() 函数返回关于文件的信息。语法fstat(file)参数file必需。规定要检查的文件。说明获取由 file 指…

Apache Cassandra和低延迟应用程序

介绍 多年来&#xff0c; Grid Dynamics拥有许多与NoSQL相关的项目&#xff0c;尤其是Apache Cassandra。 在这篇文章中&#xff0c;我们要讨论一个给我们带来挑战的项目&#xff0c;而我们在该项目中试图回答的问题今天也仍然适用。 数字营销和在线广告在2012年很受欢迎&…

Java十大简单性能优化

关于“ web scale ”这个流行词有很多炒作&#xff0c;人们花了很多时间来重新组织他们的应用程序体系结构&#xff0c;以使其系统“规模化”。 但是什么是扩展&#xff0c;我们如何确保可以扩展&#xff1f; 缩放的不同方面 上面提到的炒作主要是关于扩展负载 &#xff0c;即…

php在苹果手机上传不了图片大小,ThinkPHP后台上传图片无默认尺寸解决方法

原标题&#xff1a;ThinkPHP后台上传图片无默认尺寸解决方法随着网站移动端的日益普及&#xff0c;对后台数据的兼容性要求也越来越高。后台数据涵盖范围不断扩大的同时&#xff0c;使得程序处理能力也必须逐步提高。使用ThinkPHP进行长沙网站开发时&#xff0c;后台kindeditor…

宝塔php gd库,宝塔面板安装 EasyImag – 一款最简单图床的安装体验

近日闲逛&#xff0c;发现了一款图床&#xff0c;一款开箱即食的简单图床程序。因为没有数据库所以安装起来也是异常简单&#xff0c;我们看看功能&#xff1a;支持设置图片质量支持仅登录后上传支持QQ截图&#xff0c;剪切板上传支持在线管理(增删改查)支持上传图片转换为指定…

批准Oracle IDM中的特定Web服务

关于Web服务端点的快速发布&#xff0c;OIM和SOA在与批准有关的场景中使用了Web服务端点- 基本内容&#xff0c;但对于初学者可能有用 。 Oracle IDM与SOA套件集成并利用其提供与批准相关的功能&#xff08;说实话&#xff0c;SOA相当丰富&#xff0c;并且也被用作Web服务连接…

Oracle15001,Oracle11gR2RAC环境DBCA创建数据库报错ORA-15055ORA-15001

在Oracle 11gR2 GridInfrastructure和Database软件安装完成之后&#xff0c;执行DBCA创建数据库到30%的时候报如下错误&#xff0c;点击OK后提示忽略并问题现象:在Oracle 11gR2 GridInfrastructure和Database软件安装完成之后&#xff0c;执行DBCA创建数据库到30%的时候报如下错…

针对新手的Java EE7和Maven项目–第8部分

第1部分 &#xff0c; 第2部分 &#xff0c; 第3部分 &#xff0c; 第4部分 &#xff0c; 第5部分 &#xff0c; 第6部分 &#xff0c; 第7部分 第8部分 自上一篇文章以来&#xff0c;这一系列教程已经有很长时间了。 是时候恢复并在我们的简单项目中添加新功能了。 正…

玩Weld-Probe –一站式查看CDI的所有方面

焊接3.0.0.Alpha4被释放 &#xff0c;而我一直坐在在DevConf.CZ一间会议室。 Jozef Hartinger&#xff08; jozefhartinger &#xff09;或多或少地在几分钟前告诉我有关此最新版本的新功能的信息。 有一个特别的功能真正引起了我的注意&#xff0c;它是新的焊接探针机制。 什…

linux cmake装在自己目录下,如何在Linux下安装cmake

全部展开OpenCV 2.2和更高版本需要使用Cmake生成生成文件&#xff0c;因此需要先安装cmake. 还有其他需要先安装cmake的软件1. 在Linux环境中打开Web浏览器&#xff0c;输入URL:mac cmake gui&#xff0c;找到最新版本的位置. 通常&#xff0c;发布了两个版本的开源软件: “源分…

Java Bootstrap:Dropwizard与Spring Boot

如何在尽可能短的时间内使准备就绪的Java应用程序投入生产&#xff1f; 我不是一个早起的人&#xff0c;所以有时需要一些时间才能启动“所有系统”提示。直到不久之前&#xff0c;这对于Java应用程序来说都是正确的&#xff0c;但是与发明贪睡功能不同闹钟&#xff0c;我们将在…

物理数据模型(PDM)-概念数据模型 (CDM)-面向对象模型 (OOM):适用于已经设计好数据库表结构了。...

步骤如下&#xff1a; 一、反向生成物理数据模型PDM 开发环境 PowerDesigner 15 ,SQL Server2005 &#xff08;1&#xff09;在开始逆向生成PDM图之前&#xff0c;需要为指定的数据库创建ODBC数据源。以Windows xp操作系统为例&#xff0c;选择“开始”/“运行”命令&#xff0…

MySQL作为Kubernetes服务,可从WildFly Pod访问

Kubernetes上使用Vagrant的Java EE 7和WildFly&#xff08;技术提示&#xff03;71&#xff09;介绍了如何在使用Kubernetes和Docker托管的WildFly上运行琐碎的Java EE 7应用程序。 Java EE 7应用程序是在世界范围内交付的动手实验室 。 它使用与WildFly捆绑在一起的内存数据库…

麒麟Linux启动目录,优麒麟目录结构介绍 系统入门必备

对于Linux爱好者来说&#xff0c;深入了解Linux文件目录结构的标准和每个目录的详细功能&#xff0c;对于我们用好Linux系统至关重要&#xff0c;下面就由小编给大家介绍下优麒麟系统的目录结构&#xff0c;PS: 同样适用于其他Linux发行版。查看系统的全部目录&#xff1a;* 在…

java课堂作业(一)

1、环境变量配置参见&#xff1a;http://www.cnblogs.com/dongwenbo/p/3282014.html window ---> preference ---> java ---> installed jres ---> add jres tomcat 配置&#xff1a;myeclipse ---> preferences ---> myeclipse enterprise workbench --->…

为什么我的JVM访问的内存少于通过-Xmx指定的内存?

“嘿&#xff0c;你能来看看奇怪的东西吗&#xff1f;” 这就是我开始研究一个支持案例的方式&#xff0c;该案例将我引向了这篇博客文章。 当前的特殊问题与不同的工具报告了有关可用内存的不同数字有关。 简而言之&#xff0c;一位工程师正在研究特定应用程序的过多内存使用…

linux怎么安装高德导航软件,高德地图车机版如何安装?高德地图车机版安装教程...

高德地图车机版是高德为汽车车载机提供的一个专用版本&#xff0c;对于车主们来说有时候用手机导航实在是非常不方便&#xff0c;不仅屏幕小而且还可能中途来个电话什么的。高德地图车机版可以帮你的车载机装上导航地图&#xff0c;就算你不想买导航设备也能让你的车子为你导航…