[Java,程序死锁问题原理及解决方案] Java locked 错误
Java 语言通过 synchronized 关键字来保证原子性,这是因为每一个 ob ject 都有一个隐含的锁,这个也称作监视器对象。在进入 synchronized 之前自动获取此内部锁,而一旦离开此方式,无论是完成或者中断都会自动释放锁。显然这是一个独占锁,每个锁请求之间是互斥的。相对于众多高级锁 (Lock/ReadWriteLock 等),synchronized 的代价都比后者要高。但是 synchronzied 的语法比较简单,而且也比较容易使用和理解。Lock 一旦调用了 lock() 方法获取到锁而未正确释放的话很有可能造成死锁,所以 Lock 的释放操作总是跟在 finally 代码块里面,这在代码结构上也是一次调整和冗余。Lock 的实现已经将硬件资源用到了极致,所以未来可优化的空间不大,除非硬件有了更高的性能,但是 synchronized 只是规范的一种实现,这在不同的平台不同的硬件还有很高的提升空间,未来 Java 锁上的优化也会主要在这上面。既然 synchronzied 都不可能避免死锁产生,那么死锁情况会是经常容易出现的错误,下面具体描述死锁发生的原因及解决方法。
死锁描述
死锁是操作系统层面的一个错误,是进程死锁的简称,最早在 1965 年由 Dijkstra 在研究银行家算法时提出的,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一。
事实上,计算机世界有很多事情需要多线程方式去解决,因为这样才能最大程度上利用资源,才能体现出计算的高效。但是,实际上来说,计算机系统中有很多一次只能由一个进程使用的资源的情况,例如打印机,同时只能有一个进程控制它。在多通道程序设计环境中,若干进程往往要共享这类资源,而且一个进程所需要的资源还很有可能不止一个。因此,就会出现若干进程竞争有限资源,又推进顺序不当,从而构成无限期循环等待的局面。我们称这种状态为死锁。简单一点描述,死锁是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面。很显然,如果没有外力的作用,那么死锁涉及到的各个进程都将永远处于封锁状态。
系统发生死锁现象不仅浪费大量的系统资源,甚至导致整个系统崩溃,带来灾难性后果。所以,对于死锁问题在理论上和技术上都必须予以高度重视。
银行家算法
一个银行家如何将一定数目的资金安全地借给若干个客户,使这些客户既能借到钱完成要干的事,同时银行家又能收回全部资金而不至于破产。银行家就像一个操作系统,客户就像运行的进程,银行家的资金就是系统的资源。
银行家算法需要确保以下四点:
当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
顾客可以分期贷款, 但贷款的总数不能超过最大需求量;
当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金。
清单 1. 银行家算法实现
/*一共有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 j++) {
System.out.print(资源 + j + : + AVAILABLE[j] +
}
System.out.println();
System.out.println(各进程还需要的资源量:
for (i = 0; i i++) {
System.out.print(进程 + i + :
for (j = 0; j j++) {
System.out.print(资源 + j + : + NEED[i][j] +
}
System.out.print(/n
}
System.out.print(各进程已经得到的资源量: /n
for (i = 0; i i++) {
System.out.print(进程
System.out.print(i);
for (j = 0; j j++) {
System.out.print(资源 + j + : + ALLOCATION[i][j] +
}
System.out.print(/n
}
}
// 分配资源,并重新更新各种状态
public void changdata(int k) {
int j;
for (j = 0; j 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 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 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 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 i++)
FINISH[i] = FALSE;
for (j = 0; j 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 i++)
if (FINISH[i] == FALSE) {
System.out.print(/n 系统不安全!!! 本次资源申请不成功!/n
return 1;
}
}
System.out.print(/n 经安全性检查,系统安全,本次分配成功。/n
System.out.print(本次安全序列:
for (i = 0; i i++) {
System.out.print(进程 + temp[i] +
}
System.out.print(进程 + temp[M - 1]);
System.out.println(/n
return 0;
}
}
死锁示例
死锁问题是多线程特有的问题,它可以被认为是线程间切换消耗系统性能的一种极端情况。在死锁时,线程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待,其结果是系统任务永远无法执行完成。死锁问题是在多线程开发中应该坚决避免和杜绝的问题。
一般来说,要出现死锁问题需要满足以下条件:
1. 互斥条件:一个资源每次只能被一个线程使用。
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要破坏死锁 4 个必要条件之一中的任何一个,死锁问题就能被解决。
我们先来看一个示例,前面说过,死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。代码清单 2 所示的示例中,我们编写了一个简单的程序,它将会引起死锁发生,然后我们就会明白如何分析它。
清单 2. 死锁示例
public class ThreadDeadlock {
public static void main(String[] args) throws InterruptedException {
ob ject obj1 = new ob ject();
ob ject obj2 = new ob ject();
ob ject obj3 = new ob ject();
Thread t1 = new Thread(new SyncThread(obj1, obj2), t1
Thread t2 = new Thread(new SyncThread(obj2, obj3), t2
Thread t3 = new Thread(new SyncThread(obj3, obj1), t3
t1.start();
Thread.sleep(5000);
t2.start();
Thread.sleep(5000);
t3.start();
}
}
class SyncThread implements Runnable{
private ob ject obj1;
private ob ject obj2;
public SyncThread(ob ject o1, ob ject o2){
this.obj1=o1;
this.obj2=o2;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + acquiring lock on +obj1);
synchronized (obj1) {
System.out.println(name + acquired lock on +obj1);
work();
System.out.println(name + acquiring lock on +obj2);
synchronized (obj2) {
System.out.println(name + acquired lock on +obj2);
work();
}
System.out.println(name + released lock on +obj2);
}
System.out.println(name + released lock on +obj1);
System.out.println(name + finished execution.
}
private void work() {
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在上面的程序中同步线程正完成 Runnable 的接口,它工作的是两个对象,这两个对象向对方寻求死锁而且都在使用同步阻塞。在主函数中,我使用了三个为同步线程运行的线程,而且在其中每个线程中都有一个可共享的资源。这些线程以向第一个对象获取封锁这种方式运行。但是当它试着向第二个对象获取封锁时,它就会进入等待状态,因为它已经被另一个线程封锁住了。这样,在线程引起死锁的过程中,就形成了一个依赖于资源的循环。当我执行上面的程序时,就产生了输出,但是程序却因为死锁无法停止。输出如清单 3 所示。
清单 3. 清单 2 运行输出
t1 acquiring lock on ng.ob ject@1dd3812
t1 acquired lock on ng.ob ject@1dd3812
t2 acquiring lock on ng.ob ject@c791b9
t2 acquired lock on ng.ob ject@c791b9
t3 acquiring lock on ng.ob ject@1aa9f99
t3 acquired lock on ng.ob ject@1aa9f99
t1 acquiring lock on ng.ob ject@c791b9
t2 acquiring lock on ng.ob ject@1aa9f99
在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际所用的应用中,发现死锁并将它排除是非常难的。
死锁情况诊断
JVM 提供了一些工具可以来帮助诊断死锁的发生,如下面程序清单 4 所示,我们实现了一个死锁,然后尝试通过 jstack 命令追踪、分析死锁发生。
清单 4. 死锁代码
import java.util.concurrent.locks.ReentrantLock;
//下面演示一个简单的死锁,两个线程分别占用 south 锁和 north 锁,并同时请求对方占用的锁,导致死锁
public class DeadLock extends Thread{
protected ob ject myDirect;
static ReentrantLock south = new ReentrantLock();
static ReentrantLock north = new ReentrantLock();
public DeadLock(ob ject obj){
this.myDirect = obj;
if(myDirect==south){
this.setName(south
}else{
this.setName(north
}
}
@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();
}
}
jstack 可用于导出 Java 应用程序的线程堆栈,-l 选项用于打印锁的附加信息。我们运行 jstack 命令,输出入清单 5 和 6 所示,其中清单 5 里面可以看到线程处于运行状态,代码中调用了拥有锁投票、定时锁等候和可中断锁等候等特性的 ReentrantLock 锁机制。清单 6 直接打印出出现死锁情况,报告 north 和 sourth 两个线程互相等待资源,出现了死锁。
清单 5. jstack 运行输出 1
[root@facenode4 ~]# jstack -l 31274
2015-01-29 12:40:27
Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.45-b01 mixed mode):
Attach Listener daemon prio=10 tid=0x00007f6d3c001000 nid=
0x7a87 waiting on condition [0x0000000000000000]
ng.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
DestroyJavaVM prio=10 tid=0x00007f6da4006800 nid=
0x7a2b waiting on condition [0x0000000000000000]
ng.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
north prio=10 tid=0x00007f6da4101800 nid=
0x7a47 waiting on condition [0x00007f6d8963b000]
ng.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]
ng.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)
Low Memory Detector daemon prio=10 tid=0x00007f6da40d2800 nid=
0x7a44 runnable [0x0000000000000000]
ng.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
C2 CompilerThread1 daemon prio=10 tid=0x00007f6da40d0000 nid=
0x7a43 waiting on condition [0x0000000000000000]
ng.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
C2 CompilerThread0 daemon prio=10 tid=0x00007f6da40cd000 nid=
0x7a42 waiting on condition [0x0000000000000000]
ng.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
Signal Dispatcher daemon prio=10 tid=0x00007f6da40cb000 nid=
0x7a41 runnable [0x0000000000000000]
ng.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
Finalizer daemon prio=10 tid=0x00007f6da40af000 nid=
0x7a40 in ob ject.wait() [0x00007f6d89d44000]
ng.Thread.State: WAITING (on ob ject monitor)
at ng.ob ject.wait(Native Method)
- waiting on 0x0000000759001300 (a ng.ref.ReferenceQueue$Lock)
at ng.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
- locked 0x0000000759001300 (a ng.ref.ReferenceQueue$Lock)
at ng.ref.ReferenceQueue.remove(ReferenceQueue.java:134)
at ng.ref.Finalizer$FinalizerThread.run(Finalizer.java:171)
Locked ownable synchronizers:
- None
Reference Handler daemon prio=10 tid=0x00007f6da40ad000 nid=
0x7a3f in ob ject.wait() [0x00007f6d89e45000]
ng.Thread.State: WAITING (on ob ject monitor)
at ng.ob ject.wait(Native Method)
- waiting on 0x00000007590011d8 (a ng.ref.Reference$Lock)
at ng.ob ject.wait(ob ject.java:485)
at ng.ref.Reference$ReferenceHandler.run(Reference.java:116)
- locked 0x00000007590011d8 (a ng.ref.Reference$Lock)
Locked ownable synchronizers:
- None
VM Thread prio=10 tid=0x00007f6da40a6000 nid=0x7a3e runnable
GC task thread#0 (ParallelGC) prio=10 tid=0x00007f6da4019800 nid=0x7a2c runnable
GC task thread#1 (ParallelGC) prio=10 tid=0x00007f6da401b000 nid=0x7a2d runnable
GC task thread#2 (ParallelGC) prio=10 tid=0x00007f6da401d000 nid=0x7a2e runnable
GC task thread#3 (ParallelGC) prio=10 tid=0x00007f6da401f000 nid=0x7a2f runnable
GC task thread#4 (ParallelGC) prio=10 tid=0x00007f6da4020800 nid=0x7a30 runnable
GC task thread#5 (ParallelGC) prio=10 tid=0x00007f6da4022800 nid=0x7a31 runnable
GC task thread#6 (ParallelGC) prio=10 tid=0x00007f6da4024800 nid=0x7a32 runnable
GC task thread#7 (ParallelGC) prio=10 tid=0x00007f6da4026000 nid=0x7a33 runnable
GC task thread#8 (ParallelGC) prio=10 tid=0x00007f6da4028000 nid=0x7a34 runnable
GC task thread#9 (ParallelGC) prio=10 tid=0x00007f6da402a000 nid=0x7a35 runnable
GC task thread#10 (ParallelGC) prio=10 tid=0x00007f6da402b800 nid=0x7a36 runnable
GC task thread#11 (ParallelGC) prio=10 tid=0x00007f6da402d800 nid=0x7a37 runnable
GC task thread#12 (ParallelGC) prio=10 tid=0x00007f6da402f800 nid=0x7a38 runnable
GC task thread#13 (ParallelGC) prio=10 tid=0x00007f6da4031000 nid=0x7a39 runnable
GC task thread#14 (ParallelGC) prio=10 tid=0x00007f6da4033000 nid=0x7a3a runnable
GC task thread#15 (ParallelGC) prio=10 tid=0x00007f6da4035000 nid=0x7a3b runnable
GC task thread#16 (ParallelGC) prio=10 tid=0x00007f6da4036800 nid=0x7a3c runnable
GC task thread#17 (ParallelGC) prio=10 tid=0x00007f6da4038800 nid=0x7a3d runnable
VM Periodic Task Thread prio=10 tid=0x00007f6da40dd000 nid=0x7a45 waiting on condition
JNI global references: 886
清单 6. jstack 运行输出片段 2
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)
Found 1 deadlock.
死锁解决方案
死锁是由四个必要条件导致的,所以一般来说,只要破坏这四个必要条件中的一个条件,死锁情况就应该不会发生。
如果想要打破互斥条件,我们需要允许进程同时访问某些资源,这种方法受制于实际场景,不太容易实现条件;
打破不可抢占条件,这样需要允许进程强行从占有者那里夺取某些资源,或者简单一点理解,占有资源的进程不能再申请占有其他资源,必须释放手上的资源之后才能发起申请,这个其实也很难找到适用场景;
进程在运行前申请得到所有的资源,否则该进程不能进入准备执行状态。这个方法看似有点用处,但是它的缺点是可能导致资源利用率和进程并发性降低;
避免出现资源申请环路,即对资源事先分类编号,按号分配。这种方式可以有效提高资源的利用率和系统吞吐量,但是增加了系统开销,增大了进程对资源的占用时间。
如果我们在死锁检查时发现了死锁情况,那么就要努力消除死锁,使系统从死锁状态中恢复过来。消除死锁的几种方式:
1. 最简单、最常用的方法就是进行系统的重新启动,不过这种方法代价很大,它意味着在这之前所有的进程已经完成的计算工作都将付之东流,包括参与死锁的那些进程,以及未参与死锁的进程;
2. 撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。这时又分两种情况:一次性撤消参与死锁的全部进程,剥夺全部资源;或者逐步撤消参与死锁的进程,逐步收回死锁进程占有的资源。一般来说,选择逐步撤消的进程时要按照一定的原则进行,目的是撤消那些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程运行时的代价和与此进程相关的外部作业的代价等因素;
3. 进程回退策略,即让参与死锁的进程回退到没有发生死锁前某一点处,并由此点处继续执行,以求再次执行时不再发生死锁。虽然这是个较理想的办法,但是操作起来系统开销极大,要有堆栈这样的机构记录进程的每一步变化,以便今后的回退,有时这是无法做到的。
其实即便是商业产品,依然会有很多死锁情况的发生,例如 MySQL 数据库,它也经常容易出现死锁案例。
MySQL 死锁情况解决方法
假设我们用 Show innodb status 检查引擎状态时发现了死锁情况,如清单 7 所示。
清单 7. MySQL 死锁
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 ;;
*** WE ROLL BACK TRANSACTION (1)
我们假设涉事的数据表上面有一个索引,这次的死锁就是由于两条记录同时访问到了相同的索引造成的。
我们首先来看看 InnoDB 类型的数据表,只要能够解决索引问题,就可以解决死锁问题。MySQL 的 InnoDB 引擎是行级锁,需要注意的是,这不是对记录进行锁定,而是对索引进行锁定。在 UPDATE、DELETE 操作时,MySQL 不仅锁定 WHERE 条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的 next-key locking;
如语句 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_TIMEdate_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 的某些索引记录。
这样第一条语句锁定了 KEY_TSKTASK_MONTIME2 的记录,等待主键索引,而第二条语句则锁定了主键索引记录,而等待 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 (.)。
结束语
我们发现,死锁虽然是较早就被发现的问题,但是很多情况下我们设计的程序里还是经常发生死锁情况。我们不能只是分析如何解决死锁这类问题,还需要具体找出预防死锁的方法,这样才能从根本上解决问题。总的来说,还是需要系统架构师、程序员不断积累经验,从业务逻辑设计层面彻底消除死锁发生的可能性
- [Java,程序死锁问题原理及解决方案] Java locked 错误 相关文章:
- 爱情文章
- 亲情文章
- 友情文章
- 随笔
- 哲理
- 励志
- 范文大全
-
小米电视恢复出厂设置 [小米电视怎么恢复出厂设置教程?]
现在很多人买小米系列产品,现在小米电视卖的也很好,那么小米电视怎么恢复出厂设置教程?下面分享小米电视
【爱情文章】 日期:2020-02-16
-
美团拍店怎么做任务? 美团众包拍店任务什么意思
美团拍店是美团旗下数据采集APP,小伙伴可以通过做任务赚取奖励!随手拍下门店照片电话等即可赚钱哦!那
【爱情文章】 日期:2020-04-19
-
【魅族Pro5通话中如何查看联系人?】通话中查找联系人
当别人打电话给你询问他人的电话号码时,你还在手机上查看后再回拨或短信告知?接下来,大猫教你魅族Pro
【爱情文章】 日期:2020-04-16
-
BIOS参数无法进行设置:蚂蚁保护板参数无法设置
在进入BIOS设置后,无法修改设置的数据,导致这种情况的原因有很多种,各种情况处理方法也不同。1 设
【爱情文章】 日期:2020-04-05
-
小米手机如何批量删除联系人 小米手机如何批量删除联系人(适应
在网上看到很多刚用小米手机的朋友不知道小米手机怎么批量删除联系人,小米手机小米2、小米3、红米等等都
【爱情文章】 日期:2020-02-23
-
苹果iPhone6S开机菜单导航关闭方法 iPhone 6S
苹果iPhone6S开机菜单导航关闭方法。手机每次重启后就会出现“开机菜单导航&rdqu
【爱情文章】 日期:2020-04-16
-
vivo手机怎么设置闹钟背景图片【闹钟ONE怎么更换提醒界面背景图
很多小伙伴都不喜欢闹钟ONE系统默认的背景图片,不喜欢的话可以更换掉,那么怎么更换背景图片呢?现在小
【爱情文章】 日期:2020-04-07
-
[Word表格中内容太多不能全部显示该怎么设置?]表格内容太多不显示
在处理Word表格的时候,我们难免遇到这样的情况:即:表格单元格的文字太多,以至于该单元格无法显示全
【爱情文章】 日期:2020-04-26
-
Win8如何卸载自带播放器 360视频默认播放器
1、使用组合键win+i打开选择“控制面板”2、在控制面板的界面中在右上方的查看方式更改为“小图标”
【爱情文章】 日期:2020-03-15
-
支付宝转送福气卡没收到怎么办|小米旗舰店送福气包
支付宝转送福气卡没收到怎么办问题详情:支付宝转送福气卡,福气一直显示赠送中,好友没有收到,而自己的福
【爱情文章】 日期:2020-03-31
-
【jsp页面中插入css样式的三种方法总结】 在jsp中怎样加入css样式
本篇文章主要是对jsp页面中插入css样式的三种方法进行了总结介绍,需要的朋友可以过来参考下,希望对
【亲情文章】 日期:2020-05-29
-
室内设计原则 室内设计的原则有哪些
(一)室内装饰设计要满足使用功能要求室内设计是以创造良好的室内空间环境为宗旨,把满足人们在室内进行生
【亲情文章】 日期:2018-08-08
-
【在matlab中同一个窗口绘制多个曲线】 matlab绘制曲线的方法
matlab中绘制二维图形有一个能在同一个坐标下绘制多个图案的功能。plot(x1,y1,x2,y2
【亲情文章】 日期:2020-05-29
-
家居防潮法则之客厅篇_客厅颜色搭配法则
防潮第一步从客厅开始,绝对是最明智的选择。试想在阴雨连绵的天气,大部分人必定选择宅在家里,那么客厅就
【亲情文章】 日期:2018-08-09
-
耐用经济窗帘杆的挑选方法_窗帘杆的价格
在现代家居软装饰中,窗帘的功能已不仅仅是有遮挡阳光的功能,更重要的是窗帘能很好衬托居室的装饰风格,窗
【亲情文章】 日期:2018-08-09
-
autoCAD,2007坐标系的建立方法图解:三坐标测量仪怎么建立坐标系图解
适用于:autocad2004、autoCAD2007方法:1、打开cad软件,在cad里绘制一个矩
【亲情文章】 日期:2018-08-08
-
三星s4三种截屏小技巧总有一种适合你 华为截屏小技巧
很多时候,我们在使用手机的时候需要截屏,但是又不知道怎么做。在这里,小编给大家带来三星s4截屏方法。
【亲情文章】 日期:2020-02-12
-
[i5-4690配GTX770显卡实现高端DIY装机图文直播全过程,附配置清单
很多玩家为了提高电脑的性能,选择了自己搭配硬件进行电脑组装,今天教程网小编为大家带来一套定位2015
【亲情文章】 日期:2020-02-11
-
小度wifi插入电脑USB接口后没反应该怎么反应?_小度wifi多少钱一个
wifi是手机上网必备的伙伴,办公室没WIFI,买了个小度wifi使用几天后,插入接口窗口显示:未插
【亲情文章】 日期:2020-02-12
-
机箱前置usb30接主板哪个口_怎么分清电脑主板前置机箱接线?,连接
教你认清主板前置机箱接线,连接主板跳线,装电脑连跳线就这几步,简单吧1、电源开关(POWER)这个开
【亲情文章】 日期:2020-02-09
-
【支付宝小视频如怎么保存?】 支付宝小视频怎么保存到相册
对于今天推出的最新支付宝9 2版本中,支付宝给我们新增了小视频功能,可以实现10秒视频拍摄,这点跟微
【友情文章】 日期:2020-05-05
-
怎么按成绩分班 [巧用WPS快速进行自动分班操作]
前言:分班是教学管理信息化的一个热门话题,一般要用专门的软件,经过试验,用手头上的WPS电子表格,不
【友情文章】 日期:2020-02-29
-
养乐多最短的广告词 养乐多广告语
养乐多广告语:1一百亿个活的养乐多菌帮肠道做运动,肠子漂亮,人就美,活的活的养乐多,越活越开心。2给
【友情文章】 日期:2020-03-16
-
【支付宝删除本地证书和取消证书的区别是什么?】 支付宝电脑版
删除本地证书:可以防止他人用证书登录进行操作。删除后仍然是证书用户,只是把当前机器中安装的证书删除了
【友情文章】 日期:2020-04-07
-
十三步简单入侵个人电脑教程:如何简单入侵苹果手机
木马冰河是一款功能强大的远程控制软件,因为它的功能很强大所以成为了黑客们发动入侵的工具,2HK联盟M
【友情文章】 日期:2020-03-14
-
[一起作业怎么刷学豆]一起作业无限学豆软件
一起作业怎么刷学豆?一起作业怎么得学豆?一起作业是一款非常好用的帮助孩子学习的教育学习平台,在一起作
【友情文章】 日期:2020-03-27
-
店铺跳失率多少正常【淘宝店铺跳失率高怎么解决】
1 首页布局没有根据人群浏览习惯吸引买家眼球浏览更多的屏幕数,首页跳失率高;我们都知道淘宝店铺首页装
【友情文章】 日期:2020-03-05
-
交换机电源出现问题:交换机需要电源吗
一些外部的因素,会导致交换机不能正常工作,比如外部的供电不稳定、电源线路有问题等。而且还会由于电源问
【友情文章】 日期:2020-04-07
-
范冰冰的直播号是多少【花椒直播范冰冰直播间id是多少】
花椒直播已经和范冰冰正式签约!范爷将担任花椒直播首席体验官!那么范冰冰花椒直播间id是多少。范冰冰花
【友情文章】 日期:2020-03-07
-
电脑上做美篇图片怎么添加 电脑上的图片显示不了,用美图看看可
很多电脑用户都会遇到这样的问题,那就是电脑上的图片显示不了,可能很多网友会选择放弃这个图片,或者重新
【友情文章】 日期:2020-03-14
-
【清除顽固病毒的方法】 怎样删除电脑顽固病毒
有些病毒非常的顽固,在删除后等你重启电脑,它又来了,一起看看清除顽固病毒的方法。一、清空Intern
【生活随笔】 日期:2020-05-29
-
Redmine+SQL,SERVER在Windows下的安装 Windows下安装grub
我们一直用Redmine做管理项目和系统缺陷跟踪 最近中心开发了微信平台,并引入了电话系统,都是SQ
【生活随笔】 日期:2020-05-29
-
【CPU超频所导致的电脑系统蓝屏的有效解决方法】CPU超频
CPU超频容易损坏硬件,影响CPU的寿命。但是对于许多电脑发烧友来说,超频可以在已有的硬件基础上花少
【生活随笔】 日期:2020-02-09
-
浜崎真绪所有作品百度云盘_360云盘手机版想分享视频给所有朋友怎
可以使用云盘朋友圈,选择发送给所有的好友,这样他们都可以看到了,直是简单方便呢。图示:
【生活随笔】 日期:2020-04-07
-
【卧室床单搭配术】卧室床单适合什么颜色
众所周知,卧室是提供家人休息、睡觉的场所,其装修设计应追求舒适与宁静。随着人们生活品质生活的提升,如
【生活随笔】 日期:2018-08-09
-
【诠释LED显示屏控制系统之异步控制方式】 LED显示屏控制系统
LED显示屏控制方式有两种,一是同步控制方式,二是异步控制方式。同步控制方式是指:LED屏同步显示电
【生活随笔】 日期:2018-08-09
-
[三星UA55ES8000有网络端口(LAN)端口吗]三星UA55ES800
三星UA55ES8000有1个网络端口(LAN)端口。
【生活随笔】 日期:2020-03-22
-
非阿里巴巴集团网站购物流程_免税店购物流程
1以美团网为例,各网站实际操作流程会有不同,请以实际操作页面提示为准。如:打开美团网(),找到需要购
【生活随笔】 日期:2020-04-10
-
【怎么让美食显得更加诱人的拍摄技法】 彩蛋技法
古代皇帝用膳先得请一个人先替自己尝两口,等别人吃完过一会儿一看没事,好,这菜没被下毒,这就可以放心吃
【生活随笔】 日期:2020-03-18
-
PhotoShop制作泼墨山水风景画_水彩风景画详细步骤图
中国画以水调墨,在生宣纸上用墨色的浓淡变化绘出美丽的画面。下面我们用Photoshop将一幅照片处理
【生活随笔】 日期:2020-04-05
-
[serverx病毒怎么清理] 一键手机清理应用
第一步:开始--运行输入regedit确定进入注册表删除如下项:[HKEY_CURRENT_USER
【人生哲理】 日期:2020-05-29
-
cpu的风扇1155针与775针有什么区别? cpu和风扇都要涂硅脂吗
cpu的风扇1155针与775针有什么区别?775的4个脚的距离是7 2CM,1155的4个脚的距离
【人生哲理】 日期:2020-02-09
-
艺术字体图片 转换器 [使用彩影实现炫酷字体倒影特效]
倒影特效在很多地方都会用到,比如个人相片注释、产品展示、标题设计、网页图片、广告等。今天小编将向大家
【人生哲理】 日期:2020-05-29
-
手绘一级减速器装配图 浩辰CAD机械教程之减速器主视图绘制
在前几期的CAD教程中,我们为大家介绍了使用浩辰CAD机械2011绘制二级齿轮减速器装配图的部分技巧
【人生哲理】 日期:2018-08-08
-
电视出镜记者的运用技巧和误区【杀毒技巧及6大杀毒误区】
一、一般方法1、请升级你的杀毒软件到最新版本,保证病毒库是最新的。2、对于联网的用户,在杀毒之前请断
【人生哲理】 日期:2020-05-29
-
3d模型的黑色背面怎么变成透明_怎么把图片黑色全部变成透明
我们在单面建立前面模型的时候是希望悲鸣能透明,而不挡住后面的模型。1:建立我们需要的单面墙体,然后给
【人生哲理】 日期:2018-08-08
-
[浩辰建筑教程之轴号编辑(添补、重排、倒排轴号)]添补轴号
使用浩辰建筑软件轴网绘制和轴网标注好后,轴号有时候还需要进行处理。譬如:增加轴号,倒排轴号,或轴号的
【人生哲理】 日期:2018-08-08
-
中兴Z9,Max/Z9,Mini开箱测评:中兴手机刷机教程图解
为了满足消费者的不同需求,不少手机厂商都推出了尺寸各异的设备。3月下旬的时候,中兴在北京演艺中心召开
【人生哲理】 日期:2020-02-16
-
CAD绘制璀璨的钻石戒指:CAD钻石
这篇教程教三联的朋友们用AutoCAD绘制璀璨的钻石戒指,教程难度不大。转发过来和三联的朋友们一起学
【人生哲理】 日期:2018-08-08
-
【路由器死机的四大原因分析】 路由器桥接设置图解
经常会在网上看到诸多网友在购买宽带路由器以后,在使用过程中遇到死机频繁掉线问题,给网友带来很多不便,
【人生哲理】 日期:2020-02-16
-
Apple,Watch在表盘上添加文字与符号的方法:小车表盘上符号
很多苹果手表AppleWatch的用户都知道怎么更换表盘的图片,那么,大家知道怎么在表情上显示自己的
【励志文章】 日期:2020-03-19
-
AMD,760K与AMD,860K哪个好?AMD速龙ii,X4,860K和760K区别对比介绍|AMD速龙IIX4
近日AMD推出了新一代屏蔽核显,可超频的AMD速龙iiX4860k四核处理器,该处理器主要用户取代上
【励志文章】 日期:2020-02-09
-
美团怎么买学生票 美团团购怎么购买学生票
接下来小编就教大家美团团购购买学生票方法。1)打开美团团购进入应用后【向左拨动】功能栏,接着点击【火
【励志文章】 日期:2020-04-15
-
[H5广告的4个困境]广告字牌 困境
你有没有突然对现在市面上H5开始麻木了,形势千篇一律,内容缺乏新意?自己想做却总做不出像样的,不知道
【励志文章】 日期:2020-05-08
-
监控sd卡显示不存在 安卓手机sd卡不存在是什么原因
1、请确保你的手机中装有SD卡,且SD卡无损坏;2、请确保你的SD卡接触良好,在“设置-
【励志文章】 日期:2020-05-21
-
手机文件怎么上传到钉盘【钉钉如何在钉盘中设置上传的文件其他人
无法设置,隐私文件可以放到企业群文件夹中,只要企业群成员才可以查看;
【励志文章】 日期:2020-04-10
-
12308汽车票 [12308汽车票包车方法]
12308汽车票包车方法。想要多人结伴去游玩?或者想要同伴一起回家?一起来包车吧!时间方面更自由。那
【励志文章】 日期:2020-04-07
-
我的咖啡学习过程 mcafee mcafee,8.5i,安装过程中出现McAfee,F
确认有足够的权限启动系统服务。”按“忽略(I)”才能继续安装,但最后还是不能升级。经过不断摸索,上官
【励志文章】 日期:2020-03-29
-
【利用wps文字制作一张漂亮的座位表】如何用wps制作座位表
在如今的生活年代里,学校教室,体育赛场,公司会议,演唱会,晚会,聚会,乘车坐船等很多场所,常常涉及到
【励志文章】 日期:2020-05-25
-
【宝贝主图帮助拉动流量】 站外投放如何进行拉动流量
如果说你有去亲自去搜索过淘宝你想要的产品话,那你就要想想在一大堆产品里面吸引你去点击的主图是什么样的
【励志文章】 日期:2020-05-29
-
2022年局推动机关党建“灯下黑”“两张皮”专项整治工作
自XXX年X月以来,XX市XX局精心组织,扎实开展“灯下黑”“两张皮”问题专项整治工作,采取“四化”举措,聚焦“病灶”,合力“会诊”,强力整改,在短短两月时间内有力推动专...
【范文大全】 日期:2024-05-03
-
2022年度道德大讲堂活动主持词材料五篇(精选文档)
“道德讲堂”是在社会主义精神文明建设中探索出的行之有效的公民道德建设的重要方式。以下是范文网小编为您推荐。 道德大讲堂活动主持词1 尊敬的各位领导,各位来宾...
【范文大全】 日期:2024-05-03
-
幼儿园中层干部竞聘演讲稿5篇
幼儿园中层干部竞聘演讲稿5篇幼儿园中层干部竞聘演讲稿篇1各位领导、各位老师:我首先感谢领导、同志们的
【范文大全】 日期:2024-05-03
-
2022年度党委(党组)书记抓基层党建工作述职评议会上的
各位领导:大家新年好、上午好!根据***组织部和X、X委组织部关于开展党委(党组)书记抓基层党建工作述职评议考核工作的要求,受部领导委托,今天我们到XXX参加述职评议会,...
【范文大全】 日期:2024-05-03
-
2022年年国企党支部书记述职述廉报告(完整文档)
2021年,在XXX公司党委的正确领导下,我坚持廉字为先、干字当头,带领全体干部职工战疫情、保安全、稳生产、促发展、强党建,推动了XXX公司各项工作有序开展。本年度,本人被X...
【范文大全】 日期:2024-05-03
-
2022年度年党史学习教育专题民主生活会相互批评意见60条
1、服务中心的意识还需进一步提高。比如在统战工作如何进一步汇集各方力量,服务推动X高质量发展上,方法不多,成效不够明显。2、在一抓到底狠抓落实上还需加强。对于影响稳定...
【范文大全】 日期:2024-05-03
-
高二读书心得体会推荐3篇
高二读书心得体会推荐3篇高二读书心得体会推荐篇1最近,我读了一本名叫《钢铁是怎样炼成的》一本书,读完
【范文大全】 日期:2024-05-03
-
党史学习教育专题党课主持词范本五篇(全文完整)
一百年来,中国共产党带领中国人民进行的一切奋斗、牺牲、创造,归结起来就是一个主题:实现中华民族伟大复兴。下面是范文网小编为您推荐2021精选党史学习教育专题党课主持词...
【范文大全】 日期:2024-05-03
-
2022年度社会创新中心年度工作要点
一、以党建为统领,夯实社会组织党建群团工作一是全力构筑社会组织的发展堡垒。结合社会组织党建工作实际,调整完善社会组织综合党委、联合工会、综合妇联、联合团委的领导班...
【范文大全】 日期:2024-05-03
-
儿童节活动开幕主持词五篇大全【优秀范文】
主持人在台上表演的灵魂就表现在主持词中。在当下的中国社会,主持词在各种活动中起到的作用越来越大,主持要好好准备自己的主持词,以下是范文网小编为您推荐。 儿童...
【范文大全】 日期:2024-05-03