关闭

JAVA中死锁例子分析和上下文切换的实战

标签: java死锁多线程死锁死锁上下文切换
450人阅读 评论(0) 收藏 举报
分类:

本篇是整理《java多线程编程核心技术》和《java并发编程的艺术》中对于死锁和上下文切换的查看命令的总结。

一、死锁

1.死锁的图解

  • 死锁
    死锁是两个甚至多个线程被永久阻塞时的一种运行局面。死锁的原因:由于两个甚至多个线程相互等待对方已被锁定的资源。
    这里写图片描述

2.死锁的例子

public class DeadThreadLockTest implements  Runnable{

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    private String name;

    public void setFlag(String name){
        this.name = name;
    }

    public static void main(String[] args) throws InterruptedException {


        DeadThreadLockTest dtlt = new DeadThreadLockTest();
        dtlt.setFlag("a");
        Thread t1 = new Thread(dtlt,"t1");
        t1.start();

        Thread.sleep(1000);

        dtlt.setFlag("b");
        Thread t2 = new Thread(dtlt,"t2");
        t2.start();

    }

    @Override
    public void run() {

        if(name.equals("a")) {
            synchronized (lock1){

                System.out.println("name : " + name + "  -- " + Thread.currentThread().getName());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                synchronized (lock2){

                    System.out.println("lock1 ---> lock2");
                }

            }

        }
        if (name.equals("b")){
            synchronized (lock2){

                    System.out.println("name : " + name + "  -- " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lock1){

                        System.out.println("lock2 ---> lock1");
                    }
            }
        }
    }
}

ide一直处于run状态,运行控制台打印的结果:

#org.dufy.cha2.t2.DeadThreadLockTest
name : a  -- t1
name : b  -- t2

3.检查线程状态命令-查看是否发生死锁

(1):jps命令:显示当前所有的java进程(显示当前用户的所有java进程的PID)

F:\My_WorkSpace\idea_project>jps
1684 RemoteMavenServer
11088 Jps
2816 AppMain  #我们上面代码的执行线程id
1260
2332 Launcher

(2):jstack :当前线程的状态

F:\My_WorkSpace\idea_project>jstack -l 2816
2017-10-04 11:02:26
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode):

"DestroyJavaVM" prio=6 tid=0x000000000173e000 nid=0x1820 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"t2" prio=6 tid=0x000000000dd3c800 nid=0x22d0 waiting for monitor entry [0x000000000e92f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at org.dufy.cha2.t2.DeadThreadLockTest.run(DeadThreadLockTest.java:71)
        - waiting to lock <0x00000007d61fc418> (a java.lang.Object)
        - locked <0x00000007d61fc428> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
        - None

"t1" prio=6 tid=0x000000000dd3b800 nid=0x9d8 waiting for monitor entry [0x000000000e82f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at org.dufy.cha2.t2.DeadThreadLockTest.run(DeadThreadLockTest.java:54)
        - waiting to lock <0x00000007d61fc428> (a java.lang.Object)
        - locked <0x00000007d61fc418> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
        - None

"Finalizer" daemon prio=8 tid=0x000000000bc5f800 nid=0x268c in Object.wait() [0x000000000dc2f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000007d6084858> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
        - locked <0x00000007d6084858> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

   Locked ownable synchronizers:
        - None

"Reference Handler" daemon prio=10 tid=0x000000000bc59000 nid=0x2054 in Object.wait() [0x000000000db2f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000007d6084470> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:503)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
        - locked <0x00000007d6084470> (a java.lang.ref.Reference$Lock)

   Locked ownable synchronizers:
        - None

Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x000000000bc5f438 (object 0x00000007d61fc418, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x000000000bc5e0f8 (object 0x00000007d61fc428, a java.lang.Object),
  which is held by "t2"

Java stack information for the threads listed above:
===================================================
"t2":
        at org.dufy.cha2.t2.DeadThreadLockTest.run(DeadThreadLockTest.java:71)
        - waiting to lock <0x00000007d61fc418> (a java.lang.Object)
        - locked <0x00000007d61fc428> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:745)
"t1":
        at org.dufy.cha2.t2.DeadThreadLockTest.run(DeadThreadLockTest.java:54)
        - waiting to lock <0x00000007d61fc428> (a java.lang.Object)
        - locked <0x00000007d61fc418> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

查看线程的状态,并发现Found one Java-level deadlock:,Found 1 deadlock.可以知道线程发生了死锁!

4.总结

  • 死锁发生的条件
    互斥条件:线程对资源的访问是排他性的,如果一个线程对占用了某资源,那么其他线程必须处于等待状态,直到资源被释放。
    请求和保持条件:线程T1至少已经保持了一个资源R1占用,但又提出对另一个资源R2请求,而此时,资源R2被其他线程T2占用,于是该线程T1也必须等待,但又对自己保持的资源R1不释放。
    不剥夺条件:线程已获得的资源,在未使用完之前,不能被其他线程剥夺,只能在使用完以后由自己释放。
    环路等待条件:在死锁发生时,必然存在一个“进程-资源环形链”,即:{p0,p1,p2,…pn},进程p0(或线程)等待p1占用的资源,p1等待p2占用的资源,pn等待p0占用的资源。(最直观的理解是,p0等待p1占用的资源,而p1而在等待p0占用的资源,于是两个进程就相互等待)

  • 避免死锁

    避免嵌套封锁:这是死锁最主要的原因的,如果你已经有一个资源了就要避免封锁另一个资源。如果你运行时只有一个对象封锁,那是几乎不可能出现一个死锁局面的。

    只对有请求的进行封锁:你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。

    避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。

并发编程艺术:
(1)避免一个线程同时获取多个锁;
(2)避免一个线程在锁内同时占用多个资源,尽量保证每个锁占用一个资源;
(3)尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制;
(4)对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

更多的相关死锁的资可以点击查看 : 死锁 - ImportNew


二、上下文切换

第一步:使用jps查询当前用户的线程

jps  #命令

第二步:使用jstack命令dump线程信息

通过使用jstack查看当前pid的进程中的线程都在做什么。

#jstack 端口 > 重定向输出到dump目录下
jstack 5899 > dump/dump5899

第三步:统计所有线程处于什么状态

通过命令统计当前pid进程中线程的状态。

grep java.lang.Thread.State dump/dump5899 | awk '{print $2$3$4$5}' | sort | uniq -c

#---------------
   6 RUNNABLE
   1 TIMED_WAITING(onobjectmonitor)
   2 TIMED_WAITING(sleeping)
   2 WAITING(onobjectmonitor)
   1 WAITING(parking)

备注:
RUNNABLE:线程start()后,得到cup的时间片,执行状态;
TIMED_WAITING: sleep()方法执行后的状态;
BLOCKED : 出现在一个线程等待锁的时候;
WAITING: 线程执行了Object.wati方法后的状态;


第四步:打开dump文件查看对应状态的线程在做什么

打开dump文件,如果大量的线程处于BLOCKED 状态,查看处于BLOCKED 状态的线程在做什么,是什么原因导致线程大量处于BLOCKED 状态。
查看 WAITING 状态,为什么线程处于WAITING状态,是不是线程池中的启动线程太多,很多没有被用到。合理的设置线程池等。

第五步:找到原因,调整重试

发现有问题的线程状态,进行调整,然后重启相关服务,验证调整的结果。



如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!


欢迎访问我的csdn博客,我们一同成长!

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页http://blog.csdn.net/u010648555

0
0
查看评论

Java多线程--上下文切换

前言 本文来自方腾飞老师《Java并发编程的艺术》第一章。 并发编程的目的是为了让程序运行得更快,但是并不是启动更多的线程就能让程序最大限度地并发执行。在进行并发编程时,如果希望通过多线程执行任务让程序运行得更快,会面临非常多的挑战,比如上下文切换的问题、死锁的问题,以及受限于硬件和软件的资源限...
  • hello_worldee
  • hello_worldee
  • 2017-09-03 15:09
  • 166

Java一个简单的死锁例子

内容:一个简单的死锁例子,大概的思路:两个线程A和B,两把锁X和Y,现在A先拿到锁X,然后sleep()一段时间,我们知道sleep()是不会释放锁资源的。然后如果这段时间线程B拿到锁Y,也sleep()一段时间的话,那么等到两个线程都醒过来的话,那么将互相等待对方释放锁资源而僵持下去,陷入死锁。f...
  • u011345136
  • u011345136
  • 2015-05-16 20:07
  • 1779

上下文切换和锁

并发程序必须考虑上下文切换(context switch)的问题,即进程切换或线程切换导致的系统开销。即使是I/O密集型的服务器,也不应该使用过多的工作线程(或工作进程)。多线程服务器的一个优点是不同的线程可以同时运行在不同的CPU上。当线程的数量不大于CPU的数目时,上下文切换就不是问题了。并发程...
  • tengyft
  • tengyft
  • 2015-05-22 11:12
  • 615

java两种经典死锁例子,Lock发生死锁案列

第一种synchronized方式死锁,两个线程互相持有对方需要获取的锁。 第二种concurrent包Lock错误使用,导致死锁。 lock.unlock();释放锁使用地方不规范,导致死锁不能正常释放!
  • li396864285
  • li396864285
  • 2016-05-24 10:06
  • 10460

JVM调优案例分析与实战

一、内存溢出 1、堆内存溢出 模拟代码以及JVM设置: public class OOMTest {     //JVM设置    ...
  • u010372867
  • u010372867
  • 2016-11-28 16:47
  • 886

一个简单的Java死锁示例

在实际编程中,要尽量避免出现死锁的情况,但是让你故意写一个死锁的程序时似乎也不太简单(有公司会出这样的面试题),以下是一个简单的死锁例子,程序说明都写着类的注释里了,有点罗嗦,但是应该也还是表述清楚了的。 [code=java] /** * 一个简单的死锁类 * @author iS...
  • Hsuxu
  • Hsuxu
  • 2013-02-19 13:34
  • 22474

分析和解决JAVA 内存泄露的实战例子

这几天,一直在为Java的“内存泄露”问题纠结。Java应用程序占用的内存在不断的、有规律的上涨,最终超过了监控阈值。福尔摩 斯不得不出手了! 分析内存泄露的一般步骤       如果发现Java应用程序占用的内存出现了泄露的迹象,那么我...
  • bigtree_3721
  • bigtree_3721
  • 2016-01-20 18:21
  • 31516

系统调用,上下文切换及中断概念的汇总

仔细揣摩了一段时间.        系统调用过程,用户进程进入内核态,进程栈进入内核态栈, cpu进入内核态,cpu用户态各寄存器的值保存到内核态栈,执行内核态代码. 执行完从内核态返回到用户态,包括进程栈返回到用户态栈,cpu返回到用户态,cpu各寄存器的...
  • jeanter
  • jeanter
  • 2016-06-28 15:28
  • 1220

java中如何写一个死锁例子

你没看错,今天是要写一个死锁程序。通过自己写一个死锁例子,就能理解为什么会发生死锁。 但在写这个程序前,是不是要知道什么情况下才会发生死锁(假装我开头没说过那句话)。我们先抛开一系列复杂的业务逻辑,单纯从语法上来分析。 死锁的定义是,两个或两个以上的线程或进程在执行过程中,由于竞争资源或者由于彼此通...
  • qq_35064774
  • qq_35064774
  • 2016-06-30 21:42
  • 3183

多线程编程-减少上下文切换(1)

1、多线程编程-1-如何减少上下文切换     我们在使用多线程时,不是多线程就能提升程序的执行速度,程序在执行时,多线程 是 CPU通过给每个线程分配CPU时间片来实现的,时间片 是CPU分配给每个线程执行的时间,因时间片非常短,所以CPU通过不停地切换线程执行。 什么是上下文...
  • yxpjx
  • yxpjx
  • 2016-07-31 21:40
  • 1016
    个人资料
    • 访问:469304次
    • 积分:5972
    • 等级:
    • 排名:第5081名
    • 原创:169篇
    • 转载:16篇
    • 译文:0篇
    • 评论:161条
    联系方式
    博客专栏
    最新评论