并发编程实战死锁读书笔记之吐槽

秒速五厘米 2022-06-11 02:54 258阅读 0赞

简单顺序死锁

  1. package com.txr.TransferMoneyDemo;
  2. /**
  3. * Created by txr on 2017/7/28.
  4. */
  5. public class LeftRightDeadlock {
  6. private final Object left =new Object();
  7. private final Object right=new Object();
  8. public void leftRight(){
  9. synchronized (left){
  10. synchronized (right){
  11. dosomething();
  12. }
  13. }
  14. }
  15. public void rigthLeft(){
  16. synchronized (right){
  17. synchronized (left){
  18. dosomethingElse();
  19. }
  20. }
  21. }
  22. }

显然这样会出现死锁,比如A—>B B—->A,这个时候第一个线程获得了锁A等待获取锁B,第二个线程线程获取了锁B等待获取锁A,这样就形成了死锁

动态的锁顺序死锁

  1. package com.txr.TransferMoneyDemo;
  2. import javax.naming.InsufficientResourcesException;
  3. /**
  4. * Created by zj-db0236 on 2017/7/28.
  5. */
  6. public class LeftRightDeadlock {
  7. public void transferMoney(Account fromAcct,Account toAcct,DollarAmout amout) throws InsufficientResourcesException {
  8. synchronized (fromAcct){
  9. synchronized (toAcct){
  10. if(fromAcct.getBalance().compareTo(amout)<0)
  11. throw new InsufficientResourcesException();
  12. else{
  13. fromAcct.debit(amout);
  14. toAcct.credit(amout);
  15. }
  16. }
  17. }
  18. }
  19. }

这种死锁也很容易理解,比如 A—->B 另一个线程 B—->A

A:transferMoney(mycount ,yourAccount,1000)

B:transferMoney(yourcount ,myAccount,2000)

和上面一样会发生死锁

通过锁顺序来避免死锁

  1. private static final Object tieLock =new Object();
  2. /**
  3. * 以顺序锁的形式来转钱,System.identityHashCode是个本地方法,
  4. * 是以引用地址来计算的,HashCode是以值来计算的
  5. * @param fromAcct 转账人
  6. * @param toAcct 被转账人
  7. * @param amout 钱
  8. * @throws InsufficientResourcesException
  9. */
  10. public static void transferMoney(final Account fromAcct,
  11. final Account toAcct, final DollarAmout amout) throws InsufficientResourcesException {
  12. class Helper{
  13. public void transfer() throws InsufficientResourcesException{
  14. //转账人出账
  15. fromAcct.debit(amout);
  16. //被转账人入账
  17. toAcct.credit(amout);
  18. }
  19. }
  20. int fromHash =System.identityHashCode(fromAcct);
  21. int toHash = System.identityHashCode(toAcct);
  22. if(fromHash<toHash){
  23. synchronized (fromAcct){
  24. synchronized (toAcct){
  25. new Helper().transfer();
  26. }
  27. }
  28. }else if(fromHash>toHash) {
  29. synchronized (toAcct) {
  30. synchronized (fromAcct) {
  31. new Helper().transfer();
  32. }
  33. }
  34. }else{
  35. synchronized (tieLock){
  36. synchronized (fromAcct){
  37. synchronized (toAcct){
  38. new Helper().transfer();
  39. }
  40. }
  41. }
  42. }
  43. }

以下来坑了,书上说如下代码典型情况下会发生死锁

  1. public class IdentityHashCodeDemo {
  2. private static final int NUM_THREADS=20;
  3. private static final int NUM_ACCOUNTS=5;
  4. private static final int NUM_ITERATIONS=100000;
  5. /**
  6. * 模拟十万次转账居然会发生死锁这是为什么?
  7. * @param args
  8. */
  9. public static void main(String[] args) {
  10. final Random rnd=new Random();
  11. final Account[] accounts=new Account[NUM_ACCOUNTS];
  12. for (int i=0;i<accounts.length;i++)
  13. accounts[i]=new Account();
  14. //模拟十万次转账,不考虑账户为负数的情况
  15. class TransferThread extends Thread{
  16. @Override
  17. public void run() {
  18. for(int i=0;i<NUM_ITERATIONS;i++){
  19. int fromAcct=rnd.nextInt(NUM_ACCOUNTS);
  20. int toAcct=rnd.nextInt(NUM_ACCOUNTS);
  21. DollarAmout amout=new DollarAmout(rnd.nextInt(1000));
  22. try {
  23. //很纳闷明明用了顺序锁为什么还会很快出现死锁
  24. transferMoney(accounts[fromAcct],accounts[toAcct],amout);
  25. } catch (InsufficientResourcesException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }
  30. }
  31. for (int i=0;i<NUM_THREADS;i++)
  32. new TransferThread().start();
  33. }
  34. }

百思不得其解,最后没办法上网把源码当下来了,查看源码傻眼了,源码如是写着

  1. package net.jcip.examples;
  2. import java.util.*;
  3. import net.jcip.examples.DynamicOrderDeadlock.Account;
  4. import net.jcip.examples.DynamicOrderDeadlock.DollarAmount;
  5. /**
  6. * DemonstrateDeadlock
  7. * <p/>
  8. * Driver loop that induces deadlock under typical conditions
  9. *
  10. * @author Brian Goetz and Tim Peierls
  11. */
  12. public class DemonstrateDeadlock {
  13. private static final int NUM_THREADS = 20;
  14. private static final int NUM_ACCOUNTS = 5;
  15. private static final int NUM_ITERATIONS = 1000000;
  16. public static void main(String[] args) {
  17. final Random rnd = new Random();
  18. final Account[] accounts = new Account[NUM_ACCOUNTS];
  19. for (int i = 0; i < accounts.length; i++)
  20. accounts[i] = new Account();
  21. class TransferThread extends Thread {
  22. public void run() {
  23. for (int i = 0; i < NUM_ITERATIONS; i++) {
  24. int fromAcct = rnd.nextInt(NUM_ACCOUNTS);
  25. int toAcct = rnd.nextInt(NUM_ACCOUNTS);
  26. DollarAmount amount = new DollarAmount(rnd.nextInt(1000));
  27. try {
  28. DynamicOrderDeadlock.transferMoney(accounts[fromAcct], accounts[toAcct], amount);
  29. } catch (DynamicOrderDeadlock.InsufficientFundsException ignored) {
  30. }
  31. }
  32. }
  33. }
  34. for (int i = 0; i < NUM_THREADS; i++)
  35. new TransferThread().start();
  36. }
  37. }

看到了什么?担心你们粗心我给勾出来了

Center

简直就苦笑不得,书上压根就没有提到这个类,他就默默的用了也不说,然后我们再来看看DynamicOrderDeadlock类

  1. package net.jcip.examples;
  2. import java.util.concurrent.atomic.*;
  3. /**
  4. * DynamicOrderDeadlock
  5. * <p/>
  6. * Dynamic lock-ordering deadlock
  7. *
  8. * @author Brian Goetz and Tim Peierls
  9. */
  10. public class DynamicOrderDeadlock {
  11. // Warning: deadlock-prone!
  12. public static void transferMoney(Account fromAccount,
  13. Account toAccount,
  14. DollarAmount amount)
  15. throws InsufficientFundsException {
  16. synchronized (fromAccount) {
  17. synchronized (toAccount) {
  18. if (fromAccount.getBalance().compareTo(amount) < 0)
  19. throw new InsufficientFundsException();
  20. else {
  21. fromAccount.debit(amount);
  22. toAccount.credit(amount);
  23. }
  24. }
  25. }
  26. }
  27. static class DollarAmount implements Comparable<DollarAmount> {
  28. // Needs implementation
  29. public DollarAmount(int amount) {
  30. }
  31. public DollarAmount add(DollarAmount d) {
  32. return null;
  33. }
  34. public DollarAmount subtract(DollarAmount d) {
  35. return null;
  36. }
  37. public int compareTo(DollarAmount dollarAmount) {
  38. return 0;
  39. }
  40. }
  41. static class Account {
  42. private DollarAmount balance;
  43. private final int acctNo;
  44. private static final AtomicInteger sequence = new AtomicInteger();
  45. public Account() {
  46. acctNo = sequence.incrementAndGet();
  47. }
  48. void debit(DollarAmount d) {
  49. balance = balance.subtract(d);
  50. }
  51. void credit(DollarAmount d) {
  52. balance = balance.add(d);
  53. }
  54. DollarAmount getBalance() {
  55. return balance;
  56. }
  57. int getAcctNo() {
  58. return acctNo;
  59. }
  60. }
  61. static class InsufficientFundsException extends Exception {
  62. }
  63. }

是不是看完就很好理解为什么在大多数系统下会很快发生死锁了?好坑好坑好坑,一声不说就直接用了个错误的类,以上也说明在附有源码的时候,看书一定要对照源码看,这样能避免很多迷惑性的问题,写此博客也希望能帮到很多跟我一样有困惑的人【我百度,谷歌了很久始终没有找到有人写过这个】

发表评论

表情:
评论列表 (有 0 条评论,258人围观)

还没有评论,来说两句吧...

相关阅读

    相关

    最近研究了下rk3399的开发板,觉得这个环境真是糟糕,萤火虫算是比较大的了,最后发现内核里居然自己添加了代码不开源,看不到GPL的声明还是咋滴。技术支持也是糟糕,不是找不到人

    相关

      07年参与和负责公司架构的时候,加入了几个.NET的群,包括零度。觉得这个群总体还不错,里面的氛围也蛮好,但是由于工作性质,有时候1、2个星期不发言也有可能,最近群的规定变