存储过程 锁定并发_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/338149.shtml

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

相关文章

C语言 PK 各大编程语言

今天分享一篇关于C语言为何如此有魅力的文章&#xff0c;如果你还在学习哪门语言的路口抉择&#xff0c;建议可以认真看看~以下为CSDN译文&#xff1a;没有什么技术可以应用长达50年之久&#xff0c;除非它真的比大多数其他东西都要好用——对于一种计算机行业的技术来说尤其如…

c语言线性表拷贝,数据结构(C语言版)---线性表顺序存储表示

1、顺序表&#xff1a;线性表的顺序存储&#xff0c;用一组地址连续的存储单元存储线性表中的数据元素。1) 特点&#xff1a;随机访问&#xff0c;即通过首地址和元素序号可在时间O(1)内找到指定元素。表中元素的逻辑顺序与其物理顺序相同&#xff0c;线性表中元素的位序是从1开…

nifty ui_Nifty JUnit:在方法和类级别上使用规则

nifty ui如Nifty JUnit&#xff1a;使用临时文件一文中所示 &#xff0c;可以在JUnit测试中使用Rule &#xff0c;这是方法级别的规则。 在此示例中&#xff0c;我想显示ClassRule用于类级别规则的变体。 方法规则 Rule在测试类的每个测试方法&#xff08;就像Before &#xf…

在switch语句中,case后的标号只能是什么?

switch语句用于基于不同条件执行不同动作。语法格式&#xff1a;switch (变量表达式){case 常量1: 语句;break;case 常量2: 语句;break;case 常量3: 语句;break;...case 常量n: 语句;break;default: 语句;break;}switch语句是一个条件选择语句&#xff0c;找到相同的…

android+动画+锯齿,Android当中的防锯齿(Bitmap Canvas )

在Android中&#xff0c;目前&#xff0c;我知道有两种出现锯齿的情况。① 当我们用Canvas绘制位图的时候&#xff0c;如果对位图进行了选择&#xff0c;则位图会出现锯齿。② 在用View的RotateAnimation做动画时候&#xff0c;如果View当中包含有大量的图形&#xff0c;也会出…

osgi:install_OSGi服务测试助手:ServiceRegistrationRule

osgi:installOSGi服务测试可以是避免与悬挂的服务引用有关的问题的有效方法。 就像我在写简单服务贡献验证中所承诺的那样&#xff0c;这次我引入了一个JUnit规则 &#xff0c;该规则有助于测试组件之间的交互。 OSGi服务测试组件交互 假设我们有一个服务&#xff0c;该服务通…

C语言——结构体链表,附完整示例

引用自身的结构体&#xff0c;一个结构体中有一个或多个成员的基类型就是本结构体类型时&#xff0c;说明这个结构体可以引用自己&#xff0c;所以称作引用自身的结构体。例如下面的结构体&#xff1a;struct link{ char ch; struct link *p} a;p是一个可以指向struct link类型…

android 注解点击事件,android click事件注解

定义注解&#xff1a;package com.fyfeng.android.annotations;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;Retention(value RetentionPolicy.RUNTIME…

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

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

C语言知识总结——宏,枚举

1、define宏定义以#号开头的都是编译预处理指令&#xff0c;它们不是C语言的成分&#xff0c;但是C程序离不开它们&#xff0c;#define用来定义一个宏&#xff0c;程序在预处理阶段将用define定义的来内容进行了替换。因此在程序运行时&#xff0c;常量表中并没有用define定义的…

C语言知识总结——共用体

union 共用体&#xff08;联合体&#xff09;在进行某些算法的C语言编程的时候&#xff0c;需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术&#xff0c;几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构&#xff0c;在C语言中 以关键字union…

jboss入门_JBoss Forge NetBeans集成–入门

jboss入门JBoss Forge是构建基于Maven的Java EE项目的最快方法。 因此&#xff0c;它已经具有了令人敬畏的功能&#xff0c;使您作为开发人员的生活更加轻松。 在大多数情况下&#xff0c;使用Forge的人们可能会对创建Web应用程序感兴趣。 有很多方法可以开始使用Forge基础知识…

这几道 C/C 题涉及你的知识盲区?

8个C语言面试题&#xff0c;涉及指针、运算、函数、内存&#xff0c;看看你能做出几个&#xff01;1.gets()函数问&#xff1a;请找出下面代码里的问题&#xff1a;#include int main(void) {char buff[10];memset(buff, 0, sizeof(buff));gets(buff); //gets不检查输入的字符…

logback redis_使用Spring Boot和Logback登录到Redis

logback redis在进行集中式日志记录时&#xff0c;例如使用Elasticsearch&#xff0c;Logstash和Kibana或Graylog2&#xff0c;您可以为Java应用程序提供多个选项。 您既可以编写标准的应用程序日志&#xff0c;也可以使用Logstash解析这些日志&#xff0c;这些日志既可以直接使…

荣耀6plus+android5.1,荣耀66Plus EMUI3.0开发版5.5.1版本发布说明

本帖最后由 秀姬 于 2015-6-8 11:22 编辑EMUI 3.0_Android4.4_5.5.1版本发布说明(开发版)适配机型&#xff1a;荣耀6 / 荣耀6 Plus 全系列机型下载地址&#xff1a;荣耀6标配版支持荣耀6手机全系列机型&#xff0c;不区分制式&#xff01;标配版支持L0X的各机型升级&#xff0c…

C 与 C 的真正区别在哪里?

C 与 C 的真正区别在哪里&#xff1f;C是中餐厨师的菜刀&#xff0c;做啥菜就那一把刀&#xff0c;切菜切肉切鱼&#xff0c;都是这一把刀&#xff0c;刀工好的师傅&#xff0c;豆腐都能切成一朵花。无论你提什么概念&#xff0c;都能用指针给你做出来&#xff0c;如果不行&…

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

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

html 怎么置顶表格,表格(Table)表头固定,内容上滚【5个实例】

当表格往上滚动的时候&#xff0c;表头固定不动&#xff0c;这样可以让用户时刻看清每一列的数据含义&#xff0c;这是人性化的设计&#xff0c;充分考虑了用户使用体验。本文将通过5个实例&#xff0c;来介绍这种表格设计。用户可通过下载源码&#xff0c;直接应用于自己的项目…

C语言变量的定义包括变量存储类型和变量的什么?

C语言变量的定义包括变量存储类型和变量的名称。C语言定义变量的格式&#xff1a;“数据类型 变量名;”&#xff0c;“数据类型”表示想要存储什么类型的数据&#xff0c;“变量名”就是你想给这个变量起个什么名字&#xff0c;通常都是用字母。变量的定义定义变量的格式非常简…

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

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