线程死锁

何为死锁

两个线程相互等待对方已锁定的资源,产生死锁

如何避免死锁

其实这个问题很难回答,因为至少目前在Java领域,在语言层面是无法避免死锁的。更多的是依赖编程经验。

模拟死锁

比如有两个线程,共享一个对象。该对象持有 A、B 两份资源。线程1负责读取,线程2负责写入。
读取线程先占用A的锁,然后准备获取B的锁;写入线程先占用B的锁,再获取A的锁。
这个过程非常简单,但是一旦进入某个临界点,即读取线程等待写入线程占有的B锁。
写入线程等待读取线程占有的A锁,那么就很容易就产生了死锁。看如下代码演示。

/**
 * DeadLockDemo.java
 * com.yli.thread.demo
 */
package com.yli.thread.demo;

/**
 * 死锁模拟
 * 
 * @author yli
 * @since Ver 1.1
 * @Date 2016 2016年1月3日 下午8:29:01
 */
public class DeadLockDemo {

    public static void main(String[] args) {
        // 共享对象
        DeadLock deadObj = new DeadLock();
        // 读取线程
        DeadLockRead threadRead = new DeadLockRead(deadObj);
        // 写入线程
        DeadLockWrite threadWrite = new DeadLockWrite(deadObj);
        // 启动读取线程
        threadRead.start();
        // 启动写入线程
        threadWrite.start();
    }

    // 读取线程class
    private static class DeadLockRead extends Thread {

        private DeadLock dead;

        public DeadLockRead(DeadLock deadObj) {
            dead = deadObj;
        }

        @Override
        public void run() {
            dead.read();
        }
    }

    // 写入线程class
    private static class DeadLockWrite extends Thread {

        private DeadLock dead;

        public DeadLockWrite(DeadLock deadObj) {
            dead = deadObj;
        }

        @Override
        public void run() {
            dead.write();
        }
    }
}

// 共享对象提供read和write方法
class DeadLock {

    private Object objA = new Object();

    private Object objB = new Object();

    public void read() {
        String currentThread = Thread.currentThread().getName();
        // 先让读取线程获取objA的锁
        synchronized (objA) {
            System.out.println(currentThread + " 获得了Obj-A的锁");
            try {
                System.out.println(currentThread + " 休眠10s,让其他线程先获得Obj-B的锁!");
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 视图获得obj-B对象的锁,但write线程已经获得该对象的锁!
            synchronized (objB) {
                System.out.println(currentThread + " 获得了Obj-B的锁");
            }
        }
    }

    public void write() {
        String currentThread = Thread.currentThread().getName();

        try {
            // 写入线程先休眠300毫米,确保读取线程已经运行起来,且获得objA的锁
            Thread.sleep(300);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        // 由于读取线程获得objA的锁之后,会休眠10秒,所以写入线程有充分时间先获得objB的锁
        synchronized (objB) {
            System.out.println(currentThread + " 获得了Obj-B的锁");

            // 视图获得obj-A的锁,但read线程已经获得该对象上的锁
            synchronized (objA) {
                System.out.println(currentThread + " 获得了Obj-A的锁");
            }
        }
    }
}

检测死锁

之前介绍,在语言层面是无法阻止产生死锁的,但还好有工具为我们检测死锁提供便利,介绍两种常见方法。

方法1 jconsole 检测死锁

使用JDK bin目录下的jconsole工具,先运行上述demo,再打开该工具

这里写图片描述

点击检测死锁

点击死锁的线程

方法2 jstack 检测死锁

jconsole提供可视化窗体来检测死锁,也是JDK官方提供的非常强大的JVM故障定位工具。
但对于很多运行在linux上的应用来说,如果在未授权的情况下,通过运行在windows上的jconsole来远程连接运行在linux的JVM,可能不是很方便,特别是在很多大公司,特别是线上环境!!!你几乎没有任何机会来使用这种工具,主要是担心我们不专业,把线上直接搞崩溃。。。

所以,jstack 命令行工具就可以派上用场了,它也是JDK bin目录下的一个工具,非常强大
如果在windows环境,cmd进入命令行窗口

step1-获取JVM进程号

获取JVM进程号

如果你在linux上运行Java应用程序,也可以直接使用jsp命令,如果不允许使用该命令,那么这样
ps -ef|grep java 获取所有JVM的进程号,找到你想要的即可

step2-打印当前JVM堆栈

打印堆栈信息

如果在linux上,也可以直接运行该命令,如果不允许使用。。。那就没辙了,找相应管理员给你授权。

step3-发现死锁信息
在step2结束之后,你就拿到JVM当前堆栈信息了,在使用 jstack 命令时,因为带上了 -l 选项,所以会打印出死锁信息,如下所示。

死锁信息

如果对JVM常用工具不熟悉,可以参考:http://blog.csdn.net/lojze_ly/article/details/49498981

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值