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,一经查实,立即删除!

相关文章

POJ 1422 Air Raid (最小路径覆盖)

题意 给定一个有向图&#xff0c;在这个图上的某些点上放伞兵&#xff0c;可以使伞兵可以走到图上所有的点。且每个点只被一个伞兵走一次。问至少放多少伞兵。 思路 裸的最小路径覆盖。 最小路径覆盖 【路径覆盖】在一个有向图G(V, E<u,v>)中&#xff0c;路径覆盖就是在图…

自编码网络粒子群matlab,粒子群算法优化BP

这是数据1999 1611 1746 1277 753 323 229 7.10561325 1064 1414 1026 430 191 153 24.54521125 839 1197 857 301 168 139 …

在非托管对象中使用Spring托管Bean

即使我们想使用现有的最佳和最新技术&#xff0c;我们也必须处理遗留代码。 想象一下&#xff0c;新代码是用Spring框架的最新技术编写的&#xff0c;而旧代码根本不是用Spring编写的。 然后在非托管Spring对象中使用Spring托管Bean是我们必须处理的模式之一。 遗留代码具有非托…

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

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

Spring项目中的Netflix Archaius属性

Archaius基础 Netflix Archaius是用于管理应用程序配置的库。 考虑一个属性文件“ sample.properties”&#xff0c;其中包含一个名为“ myprop”的属性&#xff1a; mypropmyprop_value_default这是使用Archaius加载文件的方式&#xff1a; ConfigurationManager.loadCascad…

linux进程之间传递句柄

参考网上的实现&#xff0c;进行了一点改进&#xff0c;本来想用FIFO实现的&#xff0c;后来发现不行&#xff0c;必须使用系统的sendmsg函数&#xff0c;代码格式有点乱&#xff0c;不过功能可以实现&#xff0c;改天有空整理一下~~ 讲究看哈~ #include <sys/types.h>#i…

php 中间代码,PHP内核中用户函数、内部函数和中间代码的转换

昨天和一朋友在邮件中讨论这样一个问题&#xff1a;zend_internal_function&#xff0c;zend_function&#xff0c;zend_op_array这三种结构是可以相互转化的&#xff0c;这三者的转化是如何进行的呢&#xff1f; 以此文&#xff0c;总结。在函数调用的执行代码中我们会看到这样…

Byteman –用于字节码操纵的瑞士军刀

我正在JBoss的许多社区中工作&#xff0c;有很多有趣的事情要谈论&#xff0c;以至于我自己无法将自己的每一分都缠住。 这就是为什么我非常感谢有机会不时地欢迎客座博客的主要原因。 今天是Jochen Mader&#xff0c;他是以代码为中心的书呆子群的一部分。 目前&#xff0c;他…

最短路径问题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 指…

在Java中调用祖父母方法:您不能

在文章保护的重点中&#xff0c;我详细介绍了“受保护”如何扩展“包私有”访问。 我在那儿写道&#xff1a; 你能做的是 覆盖子类中的方法或 使用关键字super调用parent方法。 通常&#xff0c;这实际上是您可以使用受保护的方法完成的所有操作。 &#xff08;请注意&…

Handler(2)

andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。1)Looper: 一个线程可以产生一个Looper对象&#xff0c;由它来管理此线程里的MessageQueue(消息队列)。 2)Handler: 你可以构造Han…

OSGi服务测试助手:ServiceCollector

OSGi服务对于基于松耦合组件的系统开发非常有用。 但是&#xff0c;松散的耦合可能使得难以识别与悬挂服务引用有关的问题。 因此&#xff0c;我们通常运行集成测试以确保运行时服务组件的可用性。 为了减少此类测试所需的样板&#xff0c;我编写了一个简单的实用程序类来获取…

搭建struts2框架

struts是一个经典的MVC模式拦截器比过滤器拦截的力度更大 搭建struts2框架1.引入lib包 9个(2.3版本的)common-fileupload;common-io;common-lang3;common-logging;freemaker;javassistGA;ognl;struts2-core;xwork-core; 2. struts2.xml3. web.xml将所有的跳转都交给struts2处…

adams与matlab联合仿真天线,雷达天线模型MATLAB与ADAMS联合仿真 实验.doc

雷达天线模型MATLAB与ADAMS联合仿真 实验雷达天线模型MATLAB与ADAMS联合仿真实验1.导入雷达天线机械系统模型启动ADAMS&#xff0c;弹出如图1所示的对话框&#xff0c;选择“Open an existing database”,打开保存雷达天线模型的文件夹X:\antenna_test(注&#xff1a;X表示盘符…

【ACM】nyoj_139_我排第几个_201308062046

我排第几个时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB 难度&#xff1a;3描述 现在有"abcdefghijkl”12个字符&#xff0c;将其所有的排列中按字典序排列&#xff0c;给出任意一种排列&#xff0c;说出这个排列在所有的排列中是第几小的&#xff1f; …

Apache Cassandra和低延迟应用程序

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

php换设备登录逻辑,登录和退出登录的操作逻辑

登录功能的操作逻辑步骤如下&#xff1a;可以分为五个步骤来实现。1、进入页面先判断用户是否已经登录private void isLogin() {//查看本地是否有用户的登录信息SharedPreferences sp this.getActivity().getSharedPreferences("user_info", Context.MODE_PRIVATE);…

Visual studio代码行数统计

Visual studio代码行数统计 Visual Studio中的搜索功能支持正则表达式&#xff08;虽然语法比较诡异&#xff09;&#xff0c;我们完全可以通过正则表达式来遍历整个解决方案从而获得代码行数。 ^:b*[^:b#/].*$ 需要注意&#xff1a;#开头和/开头或者空行都不计入代码量。 如…