在我的上一个博客中,我研究了使用Java的传统synchronized关键字和锁排序来修复破碎的,死锁的余额转移示例代码。 但是,有一种替代方法称为显式锁定。

这里,将锁定机制称为显式而非隐式的想法是, 显式表示它不是Java语言的一部分,并且已编写了一些类来实现锁定功能。 另一方面, 隐式锁定可以定义为该语言的一部分,并且可以使用语言关键字synchronchized在后台实现锁定。

您可以争论明确锁定是否是一个好主意。 是否应该对Java语言进行改进,使其包括显式锁定功能,而不是向已经庞大的API中添加另一组类? 例如: trysynchronized()

显式锁定基于Lock接口及其ReentrantLock实现 。 与传统的synchronized关键字相比, Lock包含许多方法,这些方法使您对锁定有更多控制。 它具有您期望的方法,例如lock()会在代码的受保护部分中创建入口点,而unlock()会在代码中创建出口点。 它还具有tryLock()tryLock(long time,TimeUnit unit)仅当它可用且尚未被另一个线程获取时才获取tryLock() ,而tryLock(long time,TimeUnit unit)将尝试获取一个锁,如果不可用则等待指定的计时器。在放弃之前过期。

为了实现显式锁定,我首先向本系列以前的博客中使用的Account类添加了Lock接口。

public class Account implements Lock {private final int number;private int balance;private final ReentrantLock lock;public Account(int number, int openingBalance) {this.number = number;this.balance = openingBalance;this.lock = new ReentrantLock();}public void withDrawAmount(int amount) throws OverdrawnException {if (amount > balance) {throw new OverdrawnException();}balance -= amount;}public void deposit(int amount) {balance += amount;}public int getNumber() {return number;}public int getBalance() {return balance;}// ------- Lock interface implementation@Overridepublic void lock() {lock.lock();}@Overridepublic void lockInterruptibly() throws InterruptedException {lock.lockInterruptibly();}@Overridepublic Condition newCondition() {return lock.newCondition();}@Overridepublic boolean tryLock() {return lock.tryLock();}@Overridepublic boolean tryLock(long arg0, TimeUnit arg1) throws InterruptedException {return lock.tryLock(arg0, arg1);}@Overridepublic void unlock() {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}

在上面的代码中,您可以看到我赞成通过封装一个ReentrantLock对象来支持聚合, Account类将锁定功能委托给该对象。 唯一需要注意的小型GOTCHA是在unlock()实现中:

@Overridepublic void unlock() {if (lock.isHeldByCurrentThread()) {lock.unlock();}}

它具有附加的if()语句,该语句检查调用线程是否是当前持有锁的线程。 如果错过了这一行代码,那么您将获得以下IllegalMonitorStateException

Exception in thread 'Thread-7' java.lang.IllegalMonitorStateExceptionat java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)at threads.lock.Account.unlock(Account.java:76)at threads.lock.TrylockDemo$BadTransferOperation.transfer(TrylockDemo.java:98)at threads.lock.TrylockDemo$BadTransferOperation.run(TrylockDemo.java:67)

那么,这是如何实现的呢? 下面是基于我的原始DeadLockDemo程序的TryLockDemo示例的列表。

public class TrylockDemo {private static final int NUM_ACCOUNTS = 10;private static final int NUM_THREADS = 20;private static final int NUM_ITERATIONS = 100000;private static final int LOCK_ATTEMPTS = 10000;static final Random rnd = new Random();List<Account> accounts = new ArrayList<Account>();public static void main(String args[]) {TrylockDemo demo = new TrylockDemo();demo.setUp();demo.run();}void setUp() {for (int i = 0; i < NUM_ACCOUNTS; i++) {Account account = new Account(i, 1000);accounts.add(account);}}void run() {for (int i = 0; i < NUM_THREADS; i++) {new BadTransferOperation(i).start();}}class BadTransferOperation extends Thread {int threadNum;BadTransferOperation(int threadNum) {this.threadNum = threadNum;}@Overridepublic void run() {int transactionCount = 0;for (int i = 0; i < NUM_ITERATIONS; i++) {Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS));int amount = rnd.nextInt(1000);if (!toAccount.equals(fromAccount)) {boolean successfulTransfer = false;try {successfulTransfer = transfer(fromAccount, toAccount, amount);} catch (OverdrawnException e) {successfulTransfer = true;}if (successfulTransfer) {transactionCount++;}}}System.out.println("Thread Complete: " + threadNum + " Successfully made " + transactionCount + " out of "+ NUM_ITERATIONS);}private boolean transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {boolean success = false;for (int i = 0; i < LOCK_ATTEMPTS; i++) {try {if (fromAccount.tryLock()) {try {if (toAccount.tryLock()) {success = true;fromAccount.withDrawAmount(transferAmount);toAccount.deposit(transferAmount);break;}} finally {toAccount.unlock();}}} finally {fromAccount.unlock();}}return success;}}
}

想法是一样的,我有一个银行帐户列表,我将随机选择两个帐户,并从一个帐户中随机转移一个金额。 问题的核心是我更新的transfer(...)方法,如下所示。

private boolean transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {boolean success = false;for (int i = 0; i < LOCK_ATTEMPTS; i++) {try {if (fromAccount.tryLock()) {try {if (toAccount.tryLock()) {success = true;fromAccount.withDrawAmount(transferAmount);toAccount.deposit(transferAmount);break;}} finally {toAccount.unlock();}}} finally {fromAccount.unlock();}}return success;}

这里的想法是我尝试锁定fromAccount然后锁定toAccount 。 如果可行,那么我先进行转移,然后再记得解锁两个帐户。 如果那时帐户已经被锁定,那么我的tryLock()方法将失败,整个过程将循环并再次尝试。 尝试10000次锁定后,线程将放弃并忽略传输。 我猜想在现实世界的应用程序中,您希望将此故障放入某种队列中,以便以后进行调查。

在使用显式锁定时,您必须考虑它的工作原理,因此请看下面的结果…

Thread Complete: 17 Successfully made 58142 out of 100000
Thread Complete: 12 Successfully made 57627 out of 100000
Thread Complete: 9 Successfully made 57901 out of 100000
Thread Complete: 16 Successfully made 56754 out of 100000
Thread Complete: 3 Successfully made 56914 out of 100000
Thread Complete: 14 Successfully made 57048 out of 100000
Thread Complete: 8 Successfully made 56817 out of 100000
Thread Complete: 4 Successfully made 57134 out of 100000
Thread Complete: 15 Successfully made 56636 out of 100000
Thread Complete: 19 Successfully made 56399 out of 100000
Thread Complete: 2 Successfully made 56603 out of 100000
Thread Complete: 13 Successfully made 56889 out of 100000
Thread Complete: 0 Successfully made 56904 out of 100000
Thread Complete: 5 Successfully made 57119 out of 100000
Thread Complete: 7 Successfully made 56776 out of 100000
Thread Complete: 6 Successfully made 57076 out of 100000
Thread Complete: 10 Successfully made 56871 out of 100000
Thread Complete: 11 Successfully made 56863 out of 100000
Thread Complete: 18 Successfully made 56916 out of 100000
Thread Complete: 1 Successfully made 57304 out of 100000

这些表明,尽管该程序没有死锁并无限期地挂起,但它仅设法使余额转移只超过转移请求的一半。 这意味着它正在消耗大量的处理能力,包括循环,循环和循环-总体上不是很有效。 另外,我刚才说过该程序“没有死锁并无限期地挂起”,这不是真的。 如果您考虑发生了什么,那么您将意识到程序陷入僵局,然后退出这种情况。

我的显式锁定演示代码的第二个版本使用上面提到的tryLock(long time,TimeUnit unit)

private boolean transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException {boolean success = false;try {if (fromAccount.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS)) {try {if (toAccount.tryLock(LOCK_TIMEOUT, TimeUnit.MILLISECONDS)) {success = true;fromAccount.withDrawAmount(transferAmount);toAccount.deposit(transferAmount);}} finally {toAccount.unlock();}}} catch (InterruptedException e) {e.printStackTrace();} finally {fromAccount.unlock();}return success;}

在这段代码中,我用1毫秒的tryLock(...)超时替换了for循环。 这意味着,当tryLock(...)被调用并且无法获取锁定时,它将等待1 ms,然后回滚并放弃。

Thread Complete: 0 Successfully made 26637 out of 100000
Thread Complete: 14 Successfully made 26516 out of 100000
Thread Complete: 3 Successfully made 26552 out of 100000
Thread Complete: 11 Successfully made 26653 out of 100000
Thread Complete: 7 Successfully made 26399 out of 100000
Thread Complete: 1 Successfully made 26602 out of 100000
Thread Complete: 18 Successfully made 26606 out of 100000
Thread Complete: 17 Successfully made 26358 out of 100000
Thread Complete: 19 Successfully made 26407 out of 100000
Thread Complete: 16 Successfully made 26312 out of 100000
Thread Complete: 15 Successfully made 26449 out of 100000
Thread Complete: 5 Successfully made 26388 out of 100000
Thread Complete: 8 Successfully made 26613 out of 100000
Thread Complete: 2 Successfully made 26504 out of 100000
Thread Complete: 6 Successfully made 26420 out of 100000
Thread Complete: 4 Successfully made 26452 out of 100000
Thread Complete: 9 Successfully made 26287 out of 100000
Thread Complete: 12 Successfully made 26507 out of 100000
Thread Complete: 10 Successfully made 26660 out of 100000
Thread Complete: 13 Successfully made 26523 out of 100000

上面的结果表明,使用计时器时,余额转移成功率甚至下降到25%以上。 尽管现在还没有消耗大量的处理器时间,但效率仍然很低。

在相当长的一段时间内,我可能会花时间处理这两个代码示例,从而选择可以优化应用程序并提高性能的变量,但最终,没有什么真正的选择可以正确地进行锁排序。 我个人更愿意在可能的情况下使用老式的synchronized关键字隐式锁定,并为死锁代码过时,陈旧,难以理解的少数情况保留显式锁定,我已经尝试了其他所有方法,该应用需要上线,已经很晚了,该回家了……

有关更多信息,请参阅本系列中的其他博客 。

该系列以及其他博客的所有源代码都可以在Github上找到,网址为git://github.com/roghughe/captaindebug.git

参考: 调查死锁–第5部分:使用来自Captain Debug博客博客的JCG合作伙伴 Roger Hughes的显式锁定 。

翻译自: https://www.javacodegeeks.com/2012/11/investigating-deadlocks-part-5-using-explicit-locking.html

研究死锁–第5部分:使用显式锁定相关推荐

  1. 连续锁定2个不同的锁会死锁_研究死锁–第5部分:使用显式锁定

    连续锁定2个不同的锁会死锁 在我的上一个博客中,我研究了使用Java的传统synchronized关键字和锁排序来修复破碎的,死锁的余额转移示例代码. 但是,有另一种方法称为显式锁定. 在这里,将锁定 ...

  2. postgresql 并发访问_PostgreSQL并发控制(显式锁定)

    基于PostgreSQL 9.4 四.显式锁定 PostgreSQL提供了多种锁模式用于控制对表中数据的并发访问.这些模式可以用于在MVCC无法给出期望行为的场合.同样,大多数PostgreSQL命令 ...

  3. [MySQL] mysql 的行级显式锁定和悲观锁

    [MySQL] mysql 的行级显式锁定和悲观锁 隐式和显式锁定: 1.innodb是两阶段锁定协议,隐式锁定比如在事务的执行过程中.会进行锁定,锁只有在commit或rollback的时候,才会同 ...

  4. 存储过程 锁定并发_Java并发教程–锁定:显式锁定

    存储过程 锁定并发 1.简介 在许多情况下,使用隐式锁定就足够了. 有时,我们将需要更复杂的功能. 在这种情况下, java.util.concurrent.locks包为我们提供了锁定对象. 当涉及 ...

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

    1.简介 在许多情况下,使用隐式锁定就足够了. 有时,我们将需要更复杂的功能. 在这种情况下, java.util.concurrent.locks包为我们提供了锁定对象. 当涉及到内存同步时,这些锁 ...

  6. JUC-9.“锁”事(显式锁与隐式锁/悲观锁与乐观锁/公平锁与非公平锁/可重入锁/读写锁(独占/共享/降级)/邮戳锁/死锁)、锁升级

    目录 一.悲观锁与乐观锁 1.1 悲观锁 1.2 乐观锁 二.公平锁与非公平锁 2.1 为什么会有公平锁/非公平锁的设计为什么默认非公平? 2.2 如何选择使用哪种锁? 三.可重入锁(又名递归锁) 3 ...

  7. Mysql 死锁过程及案例详解之显式与隐式锁Explicit Table Lock Implicit Table Lock

    显式锁Explicit Table Lock与隐式锁Explicit Table Lock 显式锁Explicit Table Lock 显式表锁(Explicit Table Locks)即通过命令 ...

  8. 斯坦福马腾宇:用显式正则器提升深度神经网络的泛化能力

    2019年12月30日,在"智源论坛·海外学者学术报告会"上,斯坦福大学计算机科学和统计学助理教授马腾宇博士做了<为深度模型设计显式正则器>的主题演讲.马腾宇,本科就读 ...

  9. 直播预告 | 斯坦福助理教授马腾宇:深度学习中的隐式和显式正则化

    人工智能作为科技领域最具代表性的技术,日益成为国际竞争的新焦点.当下,我国正逐步开展全民智能教育普及,设置人工智能相关课程,致力于建设人工智能的人才高地. 在此背景下,中关村海华信息技术前沿研究院立足 ...

最新文章

  1. Python:高阶错误
  2. Linux tree命令以树形结构显示文件目录结构
  3. TensorFlow学习笔记(二十七)CNN的9大模型之Dan CiresanNet
  4. ubuntu常用状态查看命令
  5. Codecraft-17 and Codeforces Round #391 (Div. 1 + Div. 2, combined)
  6. Lua 学习笔记(七)编译、执行外部代码块
  7. 从社交网络图的edgelist得到adj
  8. 把SD卡制作为启动卡的操作步骤
  9. 仿瑞吉外卖 【手机登陆功能换成邮件登陆】
  10. 【c++入门(2)】贪心训练
  11. 微信小程post问题
  12. ARP和RARP协议工作原理
  13. squid回源延迟读
  14. 畅玩三子棋(可选择棋盘大小)
  15. css替换csgo弹道,csgo怎么能让弹道更明显一些?
  16. JAVA中REPLACE和REPLACEALL的区别(转)
  17. 【Linux】Linux下文件名批量编号
  18. 免费 绿色 图片编辑工具 PhotoFiltre
  19. 某MDU产品OMCI软件升级加速方案
  20. [UOJ#245][UER#7B]天路

热门文章

  1. aws dynamodb_AWS Lambda将数据保存在DynamoDB中
  2. maven原型_创建自定义Maven原型
  3. owin 怎么部署在云中_使用Boxfuse轻松在云中运行Spring Boot应用程序
  4. 使用Testcontainers和PostgreSQL,MySQL或MariaDB的Spring Boot测试
  5. 没有Javax的Jakarta EE:这次世界也不会结束
  6. 吞吐量-Corda的故事
  7. 布线问题分支限界法java_大型布线:Java云应用程序缺少的技术
  8. apollo 参数传递_使用Apollo通过WebSocket通过STOMP轻松进行消息传递
  9. adf4350配置_配置MySQL以进行ADF开发
  10. 使用RabbitMQ的SpringBoot消息传递