死锁产生和解决

面试题,之前没在意,这里学习了解下。

死锁的产生

产生死锁的原因

  1. 因为系统资源不足,并发的请求就会有竞争,产生锁;
  2. 进程运行推进的顺序和速度不合适,例如事务性的流程,会多步骤执行,如推进顺序错乱,会产生死锁;
  3. 资源分配不当等,同1。

产生死锁的四个必要条件

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

死锁的解除与预防

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。

解决方法:
1. 合理规划和分配系统资源;
2. 一次性获取所有锁;
3. 获取锁容许超时,获取某个锁失败,则释放已经持有的所有锁,重新获取锁;
4. 产生死锁,必然会有锁的获取和等待进程的环路,可以建立锁的获取列表和等待进程列表,然后单独的调度检测锁状态,检查是否有环路形成,如有,释放已经持有的锁,重新竞争,这种有点麻烦,不太常用。

死锁检测定位

这个是从参考1,那个哥们那里看到的,这里copy过来。

demo

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Kevin on 11/4/16.
 */
class MultiThread implements Runnable{
    private Object o1;
    private Object o2;
    private int flag;

    public MultiThread(Object o1, Object o2, int flag) {
        this.o1 = o1;
        this.o2 = o2;
        this.flag = flag;
    }

    public void task1() throws InterruptedException{
        synchronized (o1){
            synchronized (o2){
                System.out.println(Thread.currentThread().getName());
            }
        }
    }

    public void task2() throws InterruptedException{
        synchronized (o2){
            Thread.yield();
            synchronized (o1) {
                System.out.println(Thread.currentThread().getName());
            }
        }
    }


    @Override
    public void run() {
        while (true) {
            try {
                if (this.flag == 1) {
                    this.task1();
                } else {
                    this.task2();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class DeadLock {
    public static void main(String[] args){
        Object o1 = new Object();
        Object o2 = new Object();

        ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int flag = 0; flag < 2; flag++) {
            executorService.execute(new MultiThread(o1, o2, flag));
        }
    }
}

运行代码,你会发现控制台过一会儿就不动了,但是程序运行没有终止,下面用jstack看看到底啥问题。

$ jps
11523 Main
27894 AppMain
27912 Launcher
27933 Jps
12046 JConsole

看到pid=27894,用jstack查看一下进程27894:

$ jstack 27894
2016-11-07 14:59:55
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.101-b13 mixed mode):

"Attach Listener" #13 daemon prio=9 os_prio=0 tid=0x00007f5660001000 nid=0x6d3b runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #12 prio=5 os_prio=0 tid=0x00007f569800a800 nid=0x6cf7 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"pool-1-thread-2" #11 prio=5 os_prio=0 tid=0x00007f5698156800 nid=0x6d07 waiting for monitor entry [0x00007f5684175000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at MultiThread.task1(DeadLock.java:21)
    - waiting to lock <0x00000000ec215b70> (a java.lang.Object)
    - locked <0x00000000ec215b60> (a java.lang.Object)
    at MultiThread.run(DeadLock.java:41)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

"pool-1-thread-1" #10 prio=5 os_prio=0 tid=0x00007f5698155000 nid=0x6d06 waiting for monitor entry [0x00007f5684276000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at MultiThread.task2(DeadLock.java:30)
    - waiting to lock <0x00000000ec215b60> (a java.lang.Object)
    - locked <0x00000000ec215b70> (a java.lang.Object)
    at MultiThread.run(DeadLock.java:43)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

"Monitor Ctrl-Break" #9 daemon prio=5 os_prio=0 tid=0x00007f5698152800 nid=0x6d05 runnable [0x00007f5684377000]
   java.lang.Thread.State: RUNNABLE
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
    at java.net.ServerSocket.implAccept(ServerSocket.java:545)
    at java.net.ServerSocket.accept(ServerSocket.java:513)
    at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:90)
    at java.lang.Thread.run(Thread.java:745)

"Service Thread" #8 daemon prio=9 os_prio=0 tid=0x00007f56980da800 nid=0x6d03 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f56980bd000 nid=0x6d02 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f56980bb800 nid=0x6d01 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f56980b8800 nid=0x6d00 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f56980b7000 nid=0x6cff runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f5698084000 nid=0x6cfe in Object.wait() [0x00007f5685682000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000ec188ee0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    - locked <0x00000000ec188ee0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f569807f800 nid=0x6cfd in Object.wait() [0x00007f5685783000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000ec186b50> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    - locked <0x00000000ec186b50> (a java.lang.ref.Reference$Lock)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=0 tid=0x00007f5698078000 nid=0x6cfc runnable 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f569801f800 nid=0x6cf8 runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f5698021000 nid=0x6cf9 runnable 

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f5698023000 nid=0x6cfa runnable 

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f5698024800 nid=0x6cfb runnable 

"VM Periodic Task Thread" os_prio=0 tid=0x00007f56980dd000 nid=0x6d04 waiting on condition 

JNI global references: 15


Found one Java-level deadlock:
=============================
"pool-1-thread-2":
  waiting to lock monitor 0x00007f56680062c8 (object 0x00000000ec215b70, a java.lang.Object),
  which is held by "pool-1-thread-1"
"pool-1-thread-1":
  waiting to lock monitor 0x00007f5668004e28 (object 0x00000000ec215b60, a java.lang.Object),
  which is held by "pool-1-thread-2"

Java stack information for the threads listed above:
===================================================
"pool-1-thread-2":
    at MultiThread.task1(DeadLock.java:21)
    - waiting to lock <0x00000000ec215b70> (a java.lang.Object)
    - locked <0x00000000ec215b60> (a java.lang.Object)
    at MultiThread.run(DeadLock.java:41)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
"pool-1-thread-1":
    at MultiThread.task2(DeadLock.java:30)
    - waiting to lock <0x00000000ec215b60> (a java.lang.Object)
    - locked <0x00000000ec215b70> (a java.lang.Object)
    at MultiThread.run(DeadLock.java:43)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

发现java级别的deadlock,然后就去分析代码和场景吧!

数据库死锁解决

参考3种的内容。

  1. 尽量不要在一个事务中实现过于复杂的查询或更新操作。原因很简单,越是复杂的数据库操作,占用数据库资源的时间越长,引发死锁的可能性越大。

  2. 尽量不要在数据库事务中要求用户响应。原因同1,这也会导致事务长时间无法结束,浪费数据库资料。

  3. 死锁是由于并发访问数据库资源造成的,减少死锁就应该限制应用系统的并发访问量。我们应该合理设置后台服务的线程数,将大量数据的操作分解,分步骤,分阶段的执行。也应该避免在用户量大的时候大规模的进行后台数据库操作,应该将大规模的数据库操作放在用户量最少的时候进行。

  4. 尽可能以分区表或分区视图的方式拆分包含大量数据的表,将它们保存在不同的物量磁盘和文件组中。在访问数据时,可以分散访问保存在不同分区的数据,从而减少因为在大型表中放置锁而造成其它事务长时间等待的概率。

  5. 尽量避免使用占用很长的复杂查询,在条件允许的情况下应该尽量使用分页查询或缩小结果集的方式。因为复杂查询会长时间占用数据库资源,增加发生死锁的概率。

  6. 尽可能使用较低的隔离级别,如READ UNCOMMITTED,因为隔离级别低时,事务之间相互等待的情况会减少,这样每个事务都会尽可能快地完成数据库操作,然后释放其拥有的锁资源,这样就会降低出现锁等待或死锁的概率。当然,用户在设计数据库应用程序时,需要考虑如何解决事务中数据不一致的情况。

  7. 应该注意统一访问表的顺序,尽量避免有的事务先查询表A然后更新表B,而有的事务先查询表B再更新表A的情况。

  8. 如果一个事务中只进行读取数据的操作,则可以在该事务中使用快照(SNAPSHOT)隔离级别。因为在快照隔离级别中,数据库引擎不会阻塞其他事务对当前事务所占用资源的修改操作,当前事务会认为它所拥有的资源没有被修改过(实际上它所拥有的资源是一个快照)。这样就可以减少因为等待资源而产生死锁的情况。

参考

  1. http://blog.csdn.net/sinat_36246371/article/details/53066585 Java虚拟机工具之堆栈跟踪工具jstack检测死锁
  2. http://www.cnblogs.com/qianxun/p/3285739.html Linux下面多线程死锁问题的调试(gdb)
  3. http://www.lmwlove.com/ac/ID766 减少数据库死锁的8种方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值