/*一共有5个进程需要请求资源,有3类资源*/ public class BankDemo { // 每个进程所需要的最大资源数 public static int MAX[][] = { { 7, 5, 3 }, { 3, 2, 2 }, { 9, 0, 2 }, { 2, 2, 2 }, { 4, 3, 3 } }; // 系统拥有的初始资源数 public static int AVAILABLE[] = { 10, 5, 7 }; // 系统已给每个进程分配的资源数 public static int ALLOCATION[][] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; // 每个进程还需要的资源数 public static int NEED[][] = { { 7, 5, 3 }, { 3, 2, 2 }, { 9, 0, 2 }, { 2, 2, 2 }, { 4, 3, 3 } }; // 每次申请的资源数 public static int Request[] = { 0, 0, 0 }; // 进程数与资源数 public static int M = 5, N = 3; int FALSE = 0; int TRUE = 1; public void showdata() { int i, j; System.out.print("系统可用的资源数为:/n"); for (j = 0; j < N; j++) { System.out.print("资源" + j + ":" + AVAILABLE[j] + " "); } System.out.println(); System.out.println("各进程还需要的资源量:"); for (i = 0; i < M; i++) { System.out.print("进程" + i + ":"); for (j = 0; j < N; j++) { System.out.print("资源" + j + ":" + NEED[i][j] + " "); } System.out.print("/n"); } System.out.print("各进程已经得到的资源量: /n"); for (i = 0; i < M; i++) { System.out.print("进程"); System.out.print(i); for (j = 0; j < N; j++) { System.out.print("资源" + j + ":" + ALLOCATION[i][j] + " "); } System.out.print("/n"); } } // 分配资源,并重新更新各种状态 public void changdata(int k) { int j; for (j = 0; j < N; j++) { AVAILABLE[j] = AVAILABLE[j] - Request[j]; ALLOCATION[k][j] = ALLOCATION[k][j] + Request[j]; NEED[k][j] = NEED[k][j] - Request[j]; } }; // 回收资源,并重新更新各种状态 public void rstordata(int k) { int j; for (j = 0; j < N; j++) { AVAILABLE[j] = AVAILABLE[j] + Request[j]; ALLOCATION[k][j] = ALLOCATION[k][j] - Request[j]; NEED[k][j] = NEED[k][j] + Request[j]; } }; // 释放资源 public void free(int k) { for (int j = 0; j < N; j++) { AVAILABLE[j] = AVAILABLE[j] + ALLOCATION[k][j]; System.out.print("释放" + k + "号进程的" + j + "资源!/n"); } } public int check0(int k) { int j, n = 0; for (j = 0; j < N; j++) { if (NEED[k][j] == 0) n++; } if (n == 3) return 1; else return 0; }
// 检查安全性函数 //所以银行家算法其核心是:保证银行家系统的资源数至少不小于一个客户的所需要的资源数。在安全性检查函数 chkerr() 上由这个方法来实现 //这个循环来进行核心判断,从而完成了银行家算法的安全性检查工作。 public int chkerr(int s) { int WORK; int FINISH[] = new int[M], temp[] = new int[M];// 保存临时的安全进程序列 int i, j, k = 0; for (i = 0; i < M; i++) FINISH[i] = FALSE; for (j = 0; j < N; j++) { WORK = AVAILABLE[j]; // 第 j 个资源可用数 i = s; // 判断第 i 个进程是否满足条件 while (i < M) { if (FINISH[i] == FALSE && NEED[i][j] <= WORK) { WORK = WORK + ALLOCATION[i][j]; FINISH[i] = TRUE; temp[k] = i; k++; i = 0; } else { i++; } } for (i = 0; i < M; i++) if (FINISH[i] == FALSE) { System.out.print("/n 系统不安全!!! 本次资源申请不成功!/n"); return 1; } } System.out.print("/n 经安全性检查,系统安全,本次分配成功。/n"); System.out.print("本次安全序列:"); for (i = 0; i < M - 1; i++) { System.out.print("进程" + temp[i] + "->"); } System.out.print("进程" + temp[M - 1]); System.out.println("/n"); return 0; } }
t1 acquiring lock on java.lang.Object@1dd3812 t1 acquired lock on java.lang.Object@1dd3812 t2 acquiring lock on java.lang.Object@c791b9 t2 acquired lock on java.lang.Object@c791b9 t3 acquiring lock on java.lang.Object@1aa9f99 t3 acquired lock on java.lang.Object@1aa9f99 t1 acquiring lock on java.lang.Object@c791b9 t2 acquiring lock on java.lang.Object@1aa9f99
//下面演示一个简单的死锁,两个线程分别占用 south 锁和 north 锁,并同时请求对方占用的锁,导致死锁 public class DeadLock extends Thread{ protected Object myDirect; static ReentrantLock south = new ReentrantLock(); static ReentrantLock north = new ReentrantLock();
@Override public void run(){ if(myDirect==south){ try{ north.lockInterruptibly();//占用 north try{ Thread.sleep(500); }catch(Exception ex){ ex.printStackTrace(); } south.lockInterruptibly(); System.out.println("car to south has passed"); }catch(InterruptedException ex){ System.out.println("car to south is killed"); ex.printStackTrace(); }finally{ if(north.isHeldByCurrentThread()){ north.unlock(); } if(south.isHeldByCurrentThread()){ south.unlock(); } } } if(myDirect==north){ try{ south.lockInterruptibly();//占用 south try{ Thread.sleep(500); }catch(Exception ex){ ex.printStackTrace(); } north.lockInterruptibly(); System.out.println("car to north has passed"); }catch(InterruptedException ex){ System.out.println("car to north is killed"); ex.printStackTrace(); }finally{ if(north.isHeldByCurrentThread()){ north.unlock(); } if(south.isHeldByCurrentThread()){ south.unlock(); } } }
} public static void main(String[] args) throws InterruptedException{ DeadLock car2south = new DeadLock(south); DeadLock car2north = new DeadLock(north); car2south.start(); car2north.start(); } }
"north" prio=10 tid=0x00007f6da4101800 nid= 0x7a47 waiting on condition [0x00007f6d8963b000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000075903c7c8> ( a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156) at java.util.concurrent.locks.AbstractQueuedSynchronizer. parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) at java.util.concurrent.locks.AbstractQueuedSynchronizer. doAcquireInterruptibly(AbstractQueuedSynchronizer.java:867) at java.util.concurrent.locks.AbstractQueuedSynchronizer. acquireInterruptibly(AbstractQueuedSynchronizer.java:1201) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312) at DeadLock.run(DeadLock.java:50)
Locked ownable synchronizers: - <0x000000075903c798> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"south" prio=10 tid=0x00007f6da4100000 nid= 0x7a46 waiting on condition [0x00007f6d8973c000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000075903c798> ( a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156) at java.util.concurrent.locks.AbstractQueuedSynchronizer. parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) at java.util.concurrent.locks.AbstractQueuedSynchronizer. doAcquireInterruptibly(AbstractQueuedSynchronizer.java:867) at java.util.concurrent.locks.AbstractQueuedSynchronizer. acquireInterruptibly(AbstractQueuedSynchronizer.java:1201) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312) at DeadLock.run(DeadLock.java:28)
Locked ownable synchronizers: - <0x000000075903c7c8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"Finalizer" daemon prio=10 tid=0x00007f6da40af000 nid= 0x7a40 in Object.wait() [0x00007f6d89d44000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x0000000759001300> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - locked <0x0000000759001300> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:171)
Locked ownable synchronizers: - None
"Reference Handler" daemon prio=10 tid=0x00007f6da40ad000 nid= 0x7a3f in Object.wait() [0x00007f6d89e45000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007590011d8> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:485) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) - locked <0x00000007590011d8> (a java.lang.ref.Reference$Lock)
Found one Java-level deadlock: ============================= "north": waiting for ownable synchronizer 0x000000075903c7c8, ( a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "south" "south": waiting for ownable synchronizer 0x000000075903c798, ( a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "north"
Java stack information for the threads listed above: =================================================== "north": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000075903c7c8> ( a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156) at java.util.concurrent.locks.AbstractQueuedSynchronizer. parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) at java.util.concurrent.locks.AbstractQueuedSynchronizer. doAcquireInterruptibly(AbstractQueuedSynchronizer.java:867) at java.util.concurrent.locks.AbstractQueuedSynchronizer. acquireInterruptibly(AbstractQueuedSynchronizer.java:1201) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312) at DeadLock.run(DeadLock.java:50) "south": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000075903c798> ( a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156) at java.util.concurrent.locks.AbstractQueuedSynchronizer. parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) at java.util.concurrent.locks.AbstractQueuedSynchronizer. doAcquireInterruptibly(AbstractQueuedSynchronizer.java:867) at java.util.concurrent.locks.AbstractQueuedSynchronizer. acquireInterruptibly(AbstractQueuedSynchronizer.java:1201) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312) at DeadLock.run(DeadLock.java:28)
其实即便是商业产品,依然会有很多死锁情况的发生,例如 MySQL 数据库,它也经常容易出现死锁案例。
MySQL 死锁情况解决方法
假设我们用 Show innodb status 检查引擎状态时发现了死锁情况,如清单 7 所示。
清单 7. MySQL 死锁
1 2 3 4 5 6 7 8 9
WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 0 page no 843102 n bits 600 index `KEY_TSKTASK_MONTIME2` of table `dcnet_db/TSK_TASK` trx id 0 677833454 lock_mode X locks rec but not gap waiting Record lock, heap no 395 PHYSICAL RECORD: n_fields 3; compact format; info bits 0 0: len 8; hex 8000000000000425; asc %;; 1: len 8; hex 800012412c66d29c; asc A,f ;; 2: len 8; hex 800000000097629c; asc b ;;
如语句 UPDATE TSK_TASK SET UPDATE_TIME = NOW() WHERE ID > 10000 会锁定所有主键大于等于 1000 的所有记录,在该语句完成之前,你就不能对主键等于 10000 的记录进行操作;当非簇索引 (non-cluster index) 记录被锁定时,相关的簇索引 (cluster index) 记录也需要被锁定才能完成相应的操作。
再分析一下发生问题的两条 SQL 语句:
当”update TSK_TASK set STATUS_ID=1064,UPDATE_TIME=now () where STATUS_ID=1061 and MON_TIME<date_sub(now(), INTERVAL 30 minute)”执行时,MySQL 会使用 KEY_TSKTASK_MONTIME2 索引,因此首先锁定相关的索引记录,因为 KEY_TSKTASK_MONTIME2 是非簇索引,为执行该语句,MySQL 还会锁定簇索引(主键索引)。
假设”update TSK_TASK set STATUS_ID=1067,UPDATE_TIME=now () where ID in (9921180)”几乎同时执行时,本语句首先锁定簇索引 (主键),由于需要更新 STATUS_ID 的值,所以还需要锁定 KEY_TSKTASK_MONTIME2 的某些索引记录。
我们通过拆分第一条语句解决了死锁问题:即先查出符合条件的 ID:select ID from TSK_TASK where STATUS_ID=1061 and MON_TIME < date_sub(now(), INTERVAL 30 minute);然后再更新状态:update TSK_TASK set STATUS_ID=1064 where ID in (….)。
Reprint policy:
All articles in this blog are used except for special statements
CC BY 4.0
reprint policy. If reproduced, please indicate source
John Doe
!