并发编程实战死锁读书笔记之吐槽
2017-07-28 16:27
337 查看
简单顺序死锁
package com.txr.TransferMoneyDemo; /** * Created by txr on 2017/7/28. */ public class LeftRightDeadlock { private final Object left =new Object(); private final Object right=new Object(); public void leftRight(){ synchronized (left){ synchronized (right){ dosomething(); } } } public void rigthLeft(){ synchronized (right){ synchronized (left){ dosomethingElse(); } } } }
显然这样会出现死锁,比如A-->B B--->A,这个时候第一个线程获得了锁A等待获取锁B,第二个线程线程获取了锁B等待获取锁A,这样就形成了死锁
动态的锁顺序死锁
package com.txr.TransferMoneyDemo; import javax.naming.InsufficientResourcesException; /** * Created by zj-db0236 on 2017/7/28. */ public class LeftRightDeadlock { public void transferMoney(Account fromAcct,Account toAcct,DollarAmout amout) throws InsufficientResourcesException { synchronized (fromAcct){ synchronized (toAcct){ if(fromAcct.getBalance().compareTo(amout)<0) throw new InsufficientResourcesException(); else{ fromAcct.debit(amout); toAcct.credit(amout); } } } } }
这种死锁也很容易理解,比如 A--->B 另一个线程 B--->A
A:transferMoney(mycount ,yourAccount,1000)
B:transferMoney(yourcount ,myAccount,2000)
和上面一样会发生死锁
通过锁顺序来避免死锁
private static final Object tieLock =new Object(); /** * 以顺序锁的形式来转钱,System.identityHashCode是个本地方法, * 是以引用地址来计算的,HashCode是以值来计算的 * @param fromAcct 转账人 * @param toAcct 被转账人 * @param amout 钱 * @throws InsufficientResourcesException */ public static void transferMoney(final Account fromAcct, final Account toAcct, final DollarAmout amout) throws InsufficientResourcesException { class Helper{ public void transfer() throws InsufficientResourcesException{ //转账人出账 fromAcct.debit(amout); //被转账人入账 toAcct.credit(amout); } } int fromHash =System.identityHashCode(fromAcct); int toHash = System.identityHashCode(toAcct); if(fromHash<toHash){ synchronized (fromAcct){ synchronized (toAcct){ new Helper().transfer(); } } }else if(fromHash>toHash) { synchronized (toAcct) { synchronized (fromAcct) { new Helper().transfer(); } } }else{ synchronized (tieLock){ synchronized (fromAcct){ synchronized (toAcct){ new Helper().transfer(); } } } } }
以下来坑了,书上说如下代码典型情况下会发生死锁
public class IdentityHashCodeDemo { private static final int NUM_THREADS=20; private static final int NUM_ACCOUNTS=5; private static final int NUM_ITERATIONS=100000; /** * 模拟十万次转账居然会发生死锁这是为什么? * @param args */ public static void main(String[] args) { final Random rnd=new Random(); final Account[] accounts=new Account[NUM_ACCOUNTS]; for (int i=0;i<accounts.length;i++) accounts[i]=new Account(); //模拟十万次转账,不考虑账户为负数的情况 class TransferThread extends Thread{ @Override public void run() { for(int i=0;i<NUM_ITERATIONS;i++){ int fromAcct=rnd.nextInt(NUM_ACCOUNTS); int toAcct=rnd.nextInt(NUM_ACCOUNTS); DollarAmout amout=new DollarAmout(rnd.nextInt(1000)); try { //很纳闷明明用了顺序锁为什么还会很快出现死锁 transferMoney(accounts[fromAcct],accounts[toAcct],amout); } catch (InsufficientResourcesException e) { e.printStackTrace(); } } } } for (int i=0;i<NUM_THREADS;i++) new TransferThread().start(); } }
百思不得其解,最后没办法上网把源码当下来了,查看源码傻眼了,源码如是写着
package net.jcip.examples; import java.util.*; import net.jcip.examples.DynamicOrderDeadlock.Account; import net.jcip.examples.DynamicOrderDeadlock.DollarAmount; /** * DemonstrateDeadlock * <p/> * Driver loop that induces deadlock under typical conditions * * @author Brian Goetz and Tim Peierls */ public class DemonstrateDeadlock { private static final int NUM_THREADS = 20; private static final int NUM_ACCOUNTS = 5; private static final int NUM_ITERATIONS = 1000000; public static void main(String[] args) { final Random rnd = new Random(); final Account[] accounts = new Account[NUM_ACCOUNTS]; for (int i = 0; i < accounts.length; i++) accounts[i] = new Account(); class TransferThread extends Thread { public void run() { for (int i = 0; i < NUM_ITERATIONS; i++) { int fromAcct = rnd.nextInt(NUM_ACCOUNTS); int toAcct = rnd.nextInt(NUM_ACCOUNTS); DollarAmount amount = new DollarAmount(rnd.nextInt(1000)); try { DynamicOrderDeadlock.transferMoney(accounts[fromAcct], accounts[toAcct], amount); } catch (DynamicOrderDeadlock.InsufficientFundsException ignored) { } } } } for (int i = 0; i < NUM_THREADS; i++) new TransferThread().start(); } }看到了什么?担心你们粗心我给勾出来了
简直就苦笑不得,书上压根就没有提到这个类,他就默默的用了也不说,然后我们再来看看DynamicOrderDeadlock类
package net.jcip.examples; import java.util.concurrent.atomic.*; /** * DynamicOrderDeadlock * <p/> * Dynamic lock-ordering deadlock * * @author Brian Goetz and Tim Peierls */ public class DynamicOrderDeadlock { // Warning: deadlock-prone! public static void transferMoney(Account fromAccount, Account toAccount, DollarAmount amount) throws InsufficientFundsException { synchronized (fromAccount) { synchronized (toAccount) { if (fromAccount.getBalance().compareTo(amount) < 0) throw new InsufficientFundsException(); else { fromAccount.debit(amount); toAccount.credit(amount); } } } } static class DollarAmount implements Comparable<DollarAmount> { // Needs implementation public DollarAmount(int amount) { } public DollarAmount add(DollarAmount d) { return null; } public DollarAmount subtract(DollarAmount d) { return null; } public int compareTo(DollarAmount dollarAmount) { return 0; } } static class Account { private DollarAmount balance; private final int acctNo; private static final AtomicInteger sequence = new AtomicInteger(); public Account() { acctNo = sequence.incrementAndGet(); } void debit(DollarAmount d) { balance = balance.subtract(d); } void credit(DollarAmount d) { balance = balance.add(d); } DollarAmount getBalance() { return balance; } int getAcctNo() { return acctNo; } } static class InsufficientFundsException extends Exception { } }
是不是看完就很好理解为什么在大多数系统下会很快发生死锁了?好坑好坑好坑,一声不说就直接用了个错误的类,以上也说明在附有源码的时候,看书一定要对照源码看,这样能避免很多迷惑性的问题,写此博客也希望能帮到很多跟我一样有困惑的人【我百度,谷歌了很久始终没有找到有人写过这个】
相关文章推荐
- Java并发编程实战--协作对象间的死锁与开放调用
- 并发编程实战-读书笔记
- C++并发编程实战(读书笔记)——C++内存模型不好理解;无锁数据结构?但是等待不就是被锁住了吗??
- java并发编程实战手册第二章2.8与死锁的演示
- 【JAVA并发编程实战】8、锁顺序死锁
- Java 编程下的并发线程之间的同步代码块死锁
- java 并发编程实战 第二天
- java并发编程实战第8章
- 【Java并发编程实战】—–“J.U.C”:CountDownlatch
- 并发编程 - 死锁,活锁和饥饿
- [C#] 《Concurrency in C# Cookbook》读书笔记(一)- 并发编程概述
- java并发编程的艺术-读书笔记-01
- 并发编程实战学习笔记(九)-显式锁
- java并发编程实战-----简介
- 并发编程实战 1.3. 线程的中断 - interrupt()与isInterrupted()
- [大规模并行处理器编程实战]读书笔记_Heterogeneous Parallel Programming_CHAPTER_03
- 并发编程实战 - 性能和可伸缩性、显式锁
- Java 并发编程实战学习笔记
- 《C# 并发编程 · 经典实例》读书笔记
- Java多线程编程实战指南(核心篇)读书笔记(五)