精华内容
下载资源
问答
  • Java极客|作者/铿然一叶这是Java极客的第37篇原创文章相关阅读:一、死锁条件死锁:一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。满足死锁的四个条件:1.互斥,共享资源 X 和 Y 只能被一个线程占用2....

    65d65abcedc45954d9221779726d28a0.png

    Java极客  |  作者  /  铿然一叶

    这是Java极客的第 37 篇原创文章

    相关阅读:

    一、死锁条件

    死锁:一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。

    满足死锁的四个条件:

    1.互斥,共享资源 X 和 Y 只能被一个线程占用

    2.占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源Y的时候,不释放共享资源 X;

    3.不可抢占,其他线程不能强行抢占线程 T1占有的资源,因为不可抢占,所以要等待;

    4.循环等待,线程T1等待线程T2占有的资源,线程T2等待线程T1占有的资源,就是循环等待。

    这四个条件同时满足时,才会发生死锁,因此避免死锁只要打破其中一个条件则可。

    二、避免死锁方法

    1.对于互斥这个条件无法破坏,因为使用锁为的就是互斥。

    2.对于占有且等待,可以同时获取要使用的多个资源锁X和Y,这样就不会存在取得了X还要等待Y。这种方式只在需要获取的资源锁较少的情况下使用,如果要获取的资源锁很多(例如10个),就不太可行。

    3.对于不可抢占,可以获取了部分资源,再进一步获取其他资源时如果获取不到时,把已经获取的资源一起释放掉。此时意味着操作不能按照预期处理,需要考虑异常如何处理,例如是否需要重试。

    4.对于循环等待,可以将需要获取的锁资源排序,按照顺序获取,这样就不会多个线程交叉获取相同的资源导致死锁,而是在获取相同的资源时就等待,直到它释放。

    综上,对于极易发生死锁的场景,处理如下:

    1.获取锁时带上超时时间,获取不到就放弃,这样能最简单的避免死锁,这也意味着不能使用synchronized关键字来获得锁资源。

    2.对于已经获取到的锁资源,增加主动释放机制。

    3.放弃锁资源时增加异常流程处理,如重试。

    4.需要获取的多个锁资源排序处理,虽然前面几点可以一定程度避免死锁,但不排序的结果就是首次处理失败,重试时还可能再次失败,虽然没有发生死锁,但同一笔业务重试了N次可能也没有成功,导致无谓占用资源。

    三、死锁定位

    1.模拟死锁代码

    package com.javashizhan.concurrent.demo.deadlock;

    /**

    * @ClassName DeadlockDemo

    * @Description TODO

    * @Author 铿然一叶

    * @Date 2019/10/3 23:40

    * javashizhan.com

    **/

    public class DeadlockDemo{

    public static void main(String[] args){

    //创建两个用于加锁的对象

    final Object lockX = new Object();

    final Object lockY = new Object();

    System.out.println("lockX " + lockX);

    System.out.println("lockY " + lockY);

    Thread tX = new Thread(new Worker(lockX, lockY), "tX");

    //交换锁的顺序,模拟死锁

    Thread tY = new Thread(new Worker(lockY, lockX), "tY");

    tX.start();

    tY.start();

    }

    }

    class Worker implements Runnable{

    private final Object lockX;

    private final Object lockY;

    public Worker(Object lockX, Object lockY){

    this.lockX = lockX;

    this.lockY = lockY;

    }

    public void run(){

    synchronized (lockX) {

    //休眠一会,等待另外一个线程获取到lockY

    sleep(2000);

    System.out.println(Thread.currentThread().getName() + " get lock " + lockX);

    synchronized (lockY) {

    //这一步由于发生了死锁永远不会执行

    System.out.println(Thread.currentThread().getName() + " get lock " + lockY);

    }

    }

    }

    private void sleep(long millis){

    try {

    Thread.sleep(millis);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    复制代码

    2.执行程序输出的日志

    lockX java.lang.Object@28d93b30

    lockY java.lang.Object@1b6d3586

    tX get lock java.lang.Object@28d93b30

    tY get lock java.lang.Object@1b6d3586

    复制代码

    可以看到两个线程各自获取一个锁后发生了死锁,没有继续往下执行。

    3.jps查看java进程

    4.jstack查看java进程堆栈信息,关键部分如下:

    a0bf499089a9f31faf240e2bd71079a9.png

    可以看到有一个死锁发生,原因是tY线程和tX线程已经获取到的锁和将要获取的锁形成了循环依赖,导致死锁。

    四、解决死锁问题

    对于这个例子,死锁是因为两个锁循环依赖,根据上面描述的避免死锁方法,只要对锁排序则可,排序代码如下:

    public Worker(Object lockX, Object lockY){

    int result = lockX.toString().compareTo(lockY.toString());

    this.lockX = result == -1 ? lockX : lockY;

    this.lockY = result == -1 ? lockY : lockX;

    }

    复制代码

    代码修改后程序执行日志:

    lockX java.lang.Object@28d93b30

    lockY java.lang.Object@1b6d3586

    tX get lock java.lang.Object@1b6d3586

    tX get lock java.lang.Object@28d93b30

    tY get lock java.lang.Object@1b6d3586

    tY get lock java.lang.Object@28d93b30

    复制代码

    可以看到锁排序后,只有一个线程获取到所有锁并执行完后,另外一个线程才能获取锁,死锁问题解决。

    end.

    展开全文
  • 在游戏中有时会遇到这样一种情况,某客户端发了个请求到服务端,但收不到服务端回复,看服务端的log,也没任何错误,最后调试跟踪代码,发现代码死锁了。遇到这种情况比较纠结,于是捣腾了一个自动检测死锁的功能,...

    在游戏中有时会遇到这样一种情况,某客户端发了个请求到服务端,但收不到服务端回复,看服务端的log,也没任何错误,最后调试跟踪代码,发现代码死锁了。遇到这种情况比较纠结,于是捣腾了一个自动检测死锁的功能,如果发生死锁,会马上打印堆栈信息,并终止程序,如果是在调试环境中,会自动断点到发生死锁的地方。

    实现思路如下:

    比如Task A已经拥有了Lock 1,并准备去获取Lock 2,此时检测一下Lock 2是否被其它Task拥有了,如果没有,那Task A就很Happy的直接获取Lock 2就行了。如果Lock 2已经被Task B拥有了,那就检测一下Task B是否在等待Lock 1,如果是的话就说明是死锁了,此时打印一下堆栈信息,如果在调试环境,就中断调试,以方便查看死锁现场,否则直接退出程序。

    这样虽然上锁的效率会降低,但很快就能发现死锁。一般发布游戏到线上的时候,就把死锁检测功能去掉,也不会影响性能。

    看下我的测试代码:

    TaskMutex mutex1;

    TaskMutex mutex2;

    void m1() {

    try {

    mutex1.lock();

    sleep(1);

    mutex2.lock();

    mutex2.unlock();

    mutex1.unlock();

    } catch (...) {

    std::cout << boost::current_exception_diagnostic_information() << std::endl;

    }

    }

    void m2() {

    try {

    mutex2.lock();

    sleep(1);

    mutex1.lock();

    mutex2.unlock();

    mutex1.unlock();

    } catch (...) {

    std::cout << boost::current_exception_diagnostic_information() << std::endl;

    }

    }

    int main(int argc, char *argv[]) {

    try {

    IoScheduler scheduler(2);

    scheduler.schedule(boost::bind(&m1));

    scheduler.schedule(boost::bind(&m2));

    scheduler.stop();

    } catch (...) {

    std::cout << boost::current_exception_diagnostic_information() << std::endl;

    }

    std::cout << "will exit.." << std::endl;

    return 0;

    }

    运行结果:

    $./god_task_mutex_dead_lock

    2013-Aug-01 09:22:46.306073 FATAL god:task_mutex god/task_mutex.cpp:56 lock Deadlock found between 0x8148ca0 and 0x8148ce0

    2013-Aug-01 09:22:46.306710 FATAL : god/task_mutex.cpp:57 lock NOTREACHED

    backtrace:

    ./god_task_mutex_dead_lock() [0x80bfa78]

    ./god_task_mutex_dead_lock() [0x8054d3b]

    ./god_task_mutex_dead_lock() [0x80c733c]

    ./god_task_mutex_dead_lock() [0x80cc7c3]

    ./god_task_mutex_dead_lock() [0x80d839d]

    terminate called without an active exception

    很方便,有木有。

    展开全文
  • 所以排查定位、修复死锁至关重要;我们都知道死锁是由于多个对象或多个线程之间相互需要对方锁持有的锁而又没有释放对方所持有的锁,导致双方都永久处于阻塞状态;如上图所示,线程1持有对象1的锁、线程2持有对象2的...

    死锁应该可以说是并发编程中比较常见的一种情况,可以说如果程序产生了死锁那将会对程序带来致命的影响;所以排查定位、修复死锁至关重要;

    我们都知道死锁是由于多个对象或多个线程之间相互需要 对方锁持有的锁而又没有释放对方所持有的锁,导致双方都永久处于阻塞状态 ;

    b16886c5411e48bb9075041e571e1301.png

    如上图所示,线程1持有对象1的锁、线程2持有对象2的锁,持此线程1又想去获取对象2对象锁、线程2想获取对象1对象锁,此时由于双方都没有获取到想要的锁,任务没完成所以也没释放锁,导致一直僵持呢,于是阻塞、产生死锁;

    死锁检测

    需要检测死锁肯定要先有死锁出现,下面的demo模拟了一个死锁的产生;

    publicclassDeadlockDemoextendsThread{privateBaseObj first;privateBaseObj second;publicDeadlockDemo(String name, BaseObj first, BaseObj second){super(name);this.first = first;this.second = second;}publicvoidreentrantLock()throwsInterruptedException{    first.lock();    System.out.println(String.format("%s 持有:%s 对象锁,等待获取:%s对象锁",this.getName(), first, second));    second.lock();    first.unlock();    second.unlock();}@Overridepublicvoidrun(){try{        reentrantLock();    }catch(Exception e) {        e.printStackTrace();    }}publicstaticvoidmain(String[] args)throwsInterruptedException{    ObjOne one =newObjOne();    ObjTwo two =newObjTwo();    DeadlockDemo thread1 =newDeadlockDemo("Thread1", one, two);    DeadlockDemo thread2 =newDeadlockDemo("Thread2", two, one);    thread1.start();    thread2.start();    thread1.join();    thread2.join();} }

    运行上面的demo将看到程序被阻塞了,没法结束运行;只看到如下运行结果:

    Thread1 持有:objOne 对象锁,等待获取:objTwo对象锁 Thread2 持有:objTwo 对象锁,等待获取:objOne对象锁

    dc5825b46a7cccf36eaa215aa759cf38.png

    这demo没法结束运行就是由于产生了死锁,两个线程都在相互对待获取对方所持有的对象锁;

    这时候要解决问题就需要找出哪里出现了死锁, 通过代码走查通常不容易发现死锁 ,当然我们这程序很容易发现,因为我们刻意产生的死锁;所以就需要工具来检测死锁,这里可用的工具主要有:jconsole、jvisualvm、jstack等,这些工具其实都是jdk自带的,用法都很类似;

    这里使用jvisualvm来检测当前的demo程序是否产生了死锁;打开jvisualvm连接到当前的应用程序即可看到程序的监控信息,如内存、CPU、性能、GC等等;打开进入线程的tab项查看程序的线程信息,这里很明显的就看到了提示该程序被检测除了死锁!

    4ccf2723875dafcd464e51d953bdbc8e.png

    点击 线程Dump可以看到线程的堆栈信息,从中可以看到线程的详细信息,并定位死锁;

    cfc3d9450347cc4930931c39a00e6316.png

    从上图可以看到线程产生死锁的原因,Thrad2是等待Thread1、Thread1是等待Thread1, 从下图的堆栈信息即可定位死锁产生的位置;

    cb4cafd10d5e92f693f544daf93cc15f.png

    死锁扫描

    除了发现程序出现问题后我们去扫描死锁外,我们还可以实时的去扫描程序用于发现程序中是否存在死锁;

    JDK提供了MXBean Api可用于扫描程序是否存在死锁,ThreadMXBean提供了findDeadlockedThreads()方法,可以用于找到产生死锁的线程;这里在上面的demo程序中添加一个方法用于扫描死锁,虽然这种方法可以扫描到死锁但是由于每次都对线程打快照对程序性能会有比较大的影响,所以慎用;

    publicstaticvoidscanDeadLock(){    ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();    Runnable runnable = () -> {long[] ids = mxBean.findDeadlockedThreads();        System.out.println("扫描死锁...");if(ids !=null) {            ThreadInfo[] threadInfos = mxBean.getThreadInfo(ids);for(ThreadInfo threadInfo : threadInfos) {                System.out.println(threadInfo);            }        }    };    ScheduledExecutorService executorService =newScheduledThreadPoolExecutor(5, Executors.defaultThreadFactory());    executorService.scheduleAtFixedRate(runnable,1,5, TimeUnit.SECONDS);}

    8cfe5d7bcbe401dc3950a00c68ce0f9c.png

    避免死锁

    解决死锁最好的方法就是避免死锁了,比如上面的demo我们可以把直接使用无参数的lock()方法换为使用tryLock方法,tryLock还可以指定获取锁超时时间,到了超时时间还没获得到锁就会放弃获取锁,当然还有其它方法可以避免死锁;

    1、避免使用多个锁、长时间持有锁;

    2、设计好多个锁的获取顺序

    3、使用带超时的获取锁方法

    5b968c9f083d89373fc8e7d240ea3d24.png

    展开全文
  • java死锁排查

    2021-03-15 03:22:32
    废话不多说,直接看下面死锁代码,这是一个典型的死锁,线程1拿到A锁获取B锁,线程2拿到B锁获取A锁public class DeadLockTest{private static Object A = new Object(), B = new Object();public static void main...

    废话不多说,直接看下面死锁代码,这是一个典型的死锁,线程1拿到A锁获取B锁,线程2拿到B锁获取A锁

    public class DeadLockTest

    {

    private static Object A = new Object(), B = new Object();

    public static void main(String[] args)

    {

    new Thread(() -> {

    System.out.println("线程1开始执行...");

    synchronized (A)

    {

    try

    {

    System.out.println("线程1拿到A锁");

    //休眠两秒让线程2有时间拿到B锁

    Thread.sleep(2000);

    } catch (Exception e)

    {

    e.printStackTrace();

    }

    synchronized (B)

    {

    System.out.println("线程1拿到B锁");

    }

    }

    }).start();

    new Thread(() -> {

    System.out.println("线程2开始执行...");

    synchronized (B)

    {

    try

    {

    System.out.println("线程2拿到B锁");

    //休眠两秒让线程1有时间拿到A锁

    Thread.sleep(2000);

    } catch (Exception e)

    {

    e.printStackTrace();

    }

    synchronized (A)

    {

    System.out.println("线程2拿到A锁");

    }

    }

    }).start();

    }

    }

    我们使用编译并运行这段代码,结果一目了然了

    44ca226e7ef0de3c1e7ecd6c92202555.png

    实际上线程1永远获取不到B锁,线程2永远获取不到A锁,问题如何排查解决呢。

    方式一: 通过jps+jstack命令

    我们可以通过jps命令(jps位于jdk的bin目录下,其作用是显示当前系统的java进程情况,及其id号。)获取当前进程的id

    9bce2319e6335485f8d2df3be1214014.png

    id为6988的进程即使刚刚产生死锁的程序,我们记住这个id。使用jstack 命令去查看该线程的dump日志信息,如下图

    afa387ceafccd775d9cd6a736e34741b.png

    d203dc5fc7d07487144ba69a0a3c0b14.png

    可以看到标红的信息,此dump文件告诉了我们死锁发生的位置,我们就可以跟进代码继续去排查程序中的问题。

    方式二:通过jconsole

    在windons命令窗口 ,输出 JConsole,选择本地进程,选择DeadLockTest点下面的连接

    14a3adfcfb1229504f78a346bc412c2b.png

    点线程,点击检测死锁

    f150c3fa106c77fc8c376a7f042f4e29.png

    同样可以定位到死锁信息,以及死锁发生的位置,如下图

    50f47f96cbe9913d295b7b41a956902a.png

    方式三:使用jvisualvm

    18dd717bc3340735d7eb39f217a609f8.png

    选择线程

    434dc34ddfdca39a01afa7dc406452fb.png

    点进去我们看下信息,会发现同样定位到了死锁相关信息,以及死锁发生的位置

    1e405a3f42f4d1b433e4ac09a39db42e.png

    展开全文
  • java死锁程序

    2021-02-28 17:17:57
    (1)死锁案例/*** 一个简单的死锁类* main方法中启动两个线程,分别调用methodA和methodB方法* methodA方法首先获取到a对象的锁,睡眠1秒钟* 此时methodB方法执行获取到b对象的锁,睡眠1秒* 此时methodA需要去获取b...
  • 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够碍到满足,死锁出现的可能性就很低,否则就会因...
  • java死锁编码及定位分析 什么是死锁 死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去。如果系统资源充足,线程的资源请求都能够得到满足,...
  • 死锁死锁是一种特定的程序状态,在实体之间,由于循环依赖倒置彼此一直处于等待之中,没有任何个体可以继续前进,死锁...如果是比较明显的死锁,jstack能直接定位,类似JConsole甚至可以在图形界面进行有限的死...
  • 采用Java开发的大型应用系统越来越大,越来越复杂,很多系统集成在一起,整个系统看起来像个黑盒子。总述如何输出线程堆栈?如何解读线程堆栈?线程的解读锁的解读线程状态的解读什么是线程堆栈?线程堆栈也称线程...
  • Java如何查看死锁

    2021-02-27 07:43:19
    Java中当我们的开发涉及到多线程的时候,这个时候就很容易遇到死锁问题,刚开始遇到死锁问题的时候,我们很容易觉得莫名其妙,而且定位问题也很困难。因为涉及到java多线程的时候,有的问题会特别复杂,而且就算我们...
  • #java线程死锁的检测方法Java中线程状态:新建(New)、可运行(Runnable)、等待(Wating)、超时等待(Timed_Wating)、阻塞(Blocked)、Terminated(终止)。所有状态在Thread.state枚举类中。状态转换图: 当线程中互相等待...
  • java如何定位死锁

    2021-06-02 19:08:34
    使用arthas非常简单 运行thread -b 即可,见下图 ...测试代码如下 ... import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j;...import java.util.concurrent.ExecutorService;...import java.util.concurrent.Execu
  • 死锁是指两个或两个以上的进程在执行过程中,因争抢资源而造成的一种互相等待的现象,若无外力干涉它们将无法推进,如果系统资源充足,进程的资源请求能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的...
  • 死锁的必要条件:互斥:一份资源每次只能被一个进程或线程使用(在Java中一般体现为,一个对象锁只能被一个线程持有)请求和保持:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已经被其他进程占有,...
  • Java如何查看死锁

    2021-02-13 00:02:09
    Java中当我们的开发涉及到多线程的时候很容易遇到死锁问题,这篇文章主要解决遇到死锁问题如何定位。1、Java中jdk 给我们提供了很便利的工具,帮助我们定位和分析死锁问题:死锁产生原因:当两个或者多个线程互相...
  • 为了提升项目的响应速度,为用户提供更好的体验,原来的DAO使用的是...设计的时候就觉得,多线程下逻辑有点复杂,很可能会发生死锁,开发完成后进行测试,多线程同时进行查询、插入和删除操作,在测试程序执行了...
  • java死锁检测

    2021-02-24 20:33:08
    * @Description: java死锁测试 * 进程id查看命令:jsp 找到对应的进程id * 通过jstack 进程id 输出死锁信息 * * 如何定位死循环导致的其他线程阻塞等待: * linux下top命令查看cpu使用率较高的java进程,进而...
  • 死锁定位分析

    2021-11-14 13:38:37
    死锁:两个 或两个以上的线程执行过程中因抢夺资源而互相等待的过程 package com.example.starter; import java.util.concurrent.TimeUnit; class HoldLockThread implements Runnable{ private String lockA; ...
  • Java死锁程序

    2021-04-18 01:04:15
    另外在复杂的应用程序中,使用jstack可能不好定位,在JDK中提供了获取死锁线程的API ThreadMXBean,它的findDeadlockedThreads()方法可以查询到死锁的线程(Finds cycles of threads that are in deadlock waiting ...
  • java死锁以及解决方案

    千次阅读 2021-05-25 16:38:20
    死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因...
  • 一、分析CPU占用飙高首先写一个Java程序,并模拟一个死循环。让CPU使用率飙高。CPU负载过大的话,新的请求就处理不了了,这就是很多程序变慢了甚至不能访问的原因之一。下面是我这里的Controller,启动程序之后,开...
  • java死锁

    2021-03-16 21:22:22
    什么是死锁简单说:有一个线程A,按照先获取锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁,这个时候因为两个线程都在等待彼此手里的锁而形成了死锁。如图:死锁产生的四个...
  • javajava8java开发面试刷题18:死锁是怎么产生的?如何定位修复? 死锁是并发编程的难点问题。大家好,我是李福春,我在准备面试,今天的问题是:死锁是如何产生的?如何定位?如何修复和避免?答:死锁是一种特定的...
  • java如何查看死锁

    2021-05-24 14:06:38
    Java中当我们的开发涉及到多线程的时候,这个时候就很容易遇到死锁问题,刚开始遇到死锁问题的时候,我们很容易觉得莫名其妙,而且定位问题也很困难。 因为涉及到java多线程的时候,有的问题会特别复杂,而且就算...
  • 目录死锁是什么代码实现死锁解决办法1. 死锁是什么死锁是指两个或两个以上的进程在执行过程中因争夺资而造成的一种互相等待的现象,...2. 代码实现代码实现import java.util.concurrent.TimeUnit;class HoldLockThr...
  • 12.死锁定位

    2021-01-08 15:41:10
    死锁 两个或两个以上的进程在执行过程中,相互争抢资源造成相互等待的现象,若无外力干涉他们这种状态将一直持续下去.死锁可能会出现程序假死.当资源充足的情况下,进程的资源请求都能得到满足,出现死锁几率较低,但在...
  • 一、死锁的概念两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞的状态。我们来看一个容易造成死锁场景的例子:@Slf4jpublic class DeadLockThread implements Runnable {private String lockA;...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,971
精华内容 11,588
关键字:

java死锁定位

java 订阅