面试题之死锁解密

死锁的概念

在多线程环境中,我们经常会遇到多个线程访问同一个共享资源的情况,这个时候必须考虑如何维护数据一致性,常见的方式是加锁处理。只有拿到锁的线程才可以访问共享资源,通过锁就可以让线程对共享资源的访问都是顺序的,避免出现一些数据不一致的问题。

在使用锁的过程中同样也有风险,最为常见的就是死锁现象。死锁就相当于绳子打死结一样,解不开了。在程序中出现这样的情况往往是由于多个线程同时锁住多个资源不释放导致的。

编写一个死锁程序

public class Deadlock {
    public static String str1 = "str1";
    public static String str2 = "str2";

    public static void main(String[] args){
        Thread a = new Thread(() -> {
             try{
                 while(true){
                     synchronized(Deadlock.str1){
                         System.out.println(Thread.currentThread().getName()+"锁住 str1");
                         Thread.sleep(1000);
                         synchronized(Deadlock.str2){
                             System.out.println(Thread.currentThread().getName()+"锁住 str2");
                         }
                     }
                 }
             }catch(Exception e){
                 e.printStackTrace();
             }
        });

        Thread b = new Thread(() -> {
            try{
                while(true){
                    synchronized(Deadlock.str2){
                        System.out.println(Thread.currentThread().getName()+"锁住 str2");
                        Thread.sleep(1000);
                        synchronized(Deadlock.str1){
                             System.out.println(Thread.currentThread().getName()+"锁住 str1");
                        }
                    }
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        });

        a.start();
        b.start();
    }    
}

上面的代码就是一个完整的死锁程序,程序中有两个线程,线程1锁住了str1,获得锁之后休眠1秒钟,这个时候线程2锁住了str2,也进行休眠操作。

线程1休眠完了之后去锁str2,但是str2已经被线程2给锁住了,这边只能等待,同样的道理,线程2休眠完之后也要去锁str1,同样也会等待,这样死锁就产生了。

如果我们将下面这2行代码的值改成一样,死锁还会存在吗?

 public static String str1 = "str1";
 public static String str2 = "str1";

答案是不会,为什么? 网上看到别人的回答:在声明一个对象作为锁的时候要注意字符串类型锁对象,因为字符串有一个常量池,如果不同的线程持有的锁是具有相同字符的字符串锁时,两个锁实际上同一个锁。

如何查看死锁

首先程序不会往下执行了,这是直观的能够看到的死锁现象,看不到的我们可以通过jstack PID查看线程信息,有死锁的话在最下面会告诉我们检测到了死锁的存在,如下图:

原文发布于微信公众号 - 猿天地(cxytiandi)

原文发表时间:2018-07-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jackson0714

【.Net底层剖析】3.用IL来理解属性

3357
来自专栏JetpropelledSnake

Python面试题之Python反射机制

 我们要导入另外一个模块,可以使用import.现在有这样的需求,我动态输入一个模块名,可以随时访问到导入模块中的方法或者变量,怎么做呢?

2602
来自专栏xcywt

《Linux命令行与shell脚本编程大全》第十七章 创建函数

可以将shell脚本代码放进函数中封装起来,这样就能在脚本中的任何地方多次使用它了。 17.1 基本的脚本函数 函数:是一个脚本代码块,可以为其命名并在代码中任...

19210
来自专栏xingoo, 一个梦想做发明家的程序员

数字按照不同格式转换成字符串

  如果自己写函数,不使用itoa怎么判断呢?   我们用通常的办法,对数字进行每位的除商,得到后与字符'0'相加。 flag = 0; ...

20810
来自专栏机器学习算法与Python学习

Python的22个编程技巧,Pick一下?

Python 提供了一个直观的在一行代码中赋值与交换(变量值)的方法,请参见下面的示例:

1033
来自专栏微信公众号:Java团长

理解Java虚拟机体系结构

  众所周知,Java支持平台无关性、安全性和网络移动性。而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一的编程接口,而不管下...

1346
来自专栏Linyb极客之路

深入理解和探究Java类加载机制

java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个Java 类,即 ...

1063
来自专栏青玉伏案

窥探Swift编程之错误处理与异常抛出

在Swift 2.0版本中,Swift语言对其错误处理进行了新的设计,当然了,重新设计后的结果使得该错误处理系统用起来更爽。今天博客的主题就是系统的搞一下Swi...

1995
来自专栏轮子工厂

Java多线程学习

提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

1342
来自专栏菩提树下的杨过

bash/shell编程学习(1)

1)定义变量 myvar=abc #注:等号前后不能加空格 #或 myvar="abc" #或 myvar='abc' #注:如果变量后面的值中间本身没有空格,...

1949

扫码关注云+社区

领取腾讯云代金券