java中多线程产生死锁的原因以及解决意见

1.  java中导致死锁的原因

  多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。这是从网上其他文档看到的死锁产生的四个必要条件:

  • 1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
  • 2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  • 3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  • 4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。下面用java代码来模拟一下死锁的产生。

模拟两个资源:

public class ThreadResource
{
    public static Object resource1 = new Object();

    public static Object resource2 = new Object();
}

模拟线程1占用资源1并申请获得资源2的锁:

public class Thread1 implements Runnable
{

    @Override
    public void run()
    {
        try
        {
            System.out.println("Thread1 is running");
            synchronized (ThreadResource.resource1)
            {
                System.out.println("Thread1 lock resource1");
                Thread.sleep(2000);//休眠2s等待线程2锁定资源2
                synchronized (ThreadResource.resource2)
                {
                    System.out.println("Thread1 lock resource2");
                }
                System.out.println("Thread1 release resource2");
            }
            System.out.println("Thread1 release resource1");
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }
        System.out.println("Thread1 is stop");
    }

}

模拟线程2占用资源2并申请获得资源1的锁:

public class Thread2 implements Runnable
{

    @Override
    public void run()
    {
        try
        {
            System.out.println("Thread2 is running");
            synchronized (ThreadResource.resource2)
            {
                System.out.println("Thread2 lock resource2");
                Thread.sleep(2000);//休眠2s等待线程1锁定资源1
                synchronized (ThreadResource.resource1)
                {
                    System.out.println("Thread2 lock resource1");
                }
                System.out.println("Thread2 release resource1");
            }
            System.out.println("Thread2 release resource2");
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }
        System.out.println("Thread2 is stop");
    }

}

同时运行俩个线程:

public class ThreadTest
{
    public static void main(String[] args)
    {
       new Thread(new Thread1()).start();
       new Thread(new Thread2()).start();
    }
}

最后输出结果是:

Thread1 is running
Thread2 is running
Thread1 lock resource1
Thread2 lock resource2

并且程序一直无法结束。这就是由于线程1占用了资源1,此时线程2已经占用资源2,。这个时候线程1想要使用资源2,线程2想要使用资源1,。两个线程都无法让步,导致程序死锁。

2.  java避免死锁的解决意见

由上面的例子可以看出当线程在同步某个对象里,再去锁定另外一个对象的话,就和容易发生死锁的情况。最好是线程每次只锁定一个对象并且在锁定该对象的过程中不再去锁定其他的对象,这样就不会导致死锁了。比如将以上的线程改成下面这种写法就可以避免死锁:

public void run()
    {
        try
        {
            System.out.println("Thread1 is running");
            synchronized (ThreadResource.resource1)
            {
                System.out.println("Thread1 lock resource1");
                Thread.sleep(2000);//休眠2s等待线程2锁定资源2
            }
            System.out.println("Thread1 release resource1");
            synchronized (ThreadResource.resource2)
            {
                System.out.println("Thread1 lock resource2");
            }
            System.out.println("Thread1 release resource2");
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }
        System.out.println("Thread1 is stop");
    }

但是有的时候业务需要同时去锁定两个对象,比如转账业务:A给B转账,需要同时锁定A、B两个账户。如果A、B相互同时转账的话就会出现死锁的情况。这时可以定义一个规则:锁定账户先后的规则。根据账户的某一个属性(比如id或者hasCode),判断锁定的先后。即每一次转账业务都是先锁定A再锁定B(或者先锁定B在锁定A),这样也不会导致死锁的发生。比如按照上面的例子,需要同时锁定两个资源,可以根据资源的hashcode值大小来判断先后锁定顺序。可以这样改造线程:

public class Thread3 implements Runnable
{

    @Override
    public void run()
    {
        try
        {
            System.out.println("Thread is running");
            if ( ThreadResource.resource1.hashCode() > ThreadResource.resource2.hashCode() )
            {
                //先锁定resource1
                synchronized (ThreadResource.resource1)
                {
                    System.out.println("Thread lock resource1");
                    Thread.sleep(2000);
                    synchronized (ThreadResource.resource2)
                    {
                        System.out.println("Thread lock resource2");
                    }
                    System.out.println("Thread release resource2");
                }
                System.out.println("Thread release resource1");
            }
            else
            {
                //先锁定resource2
                synchronized (ThreadResource.resource2)
                {
                    System.out.println("Thread lock resource2");
                    Thread.sleep(2000);
                    synchronized (ThreadResource.resource1)
                    {
                        System.out.println("Thread lock resource1");
                    }
                    System.out.println("Thread release resource1");
                }
                System.out.println("Thread release resource2");
            }
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }
        System.out.println("Thread1 is stop");
    }

}

总结:死锁常见于,线程在锁定对象还没释放时,又需要锁定另一个对象,并且此时该对象可能被另一个线程锁定。这种时候很容易导致死锁。因此在开发时需要慎重使用锁,尤其是需要注意尽量不要在锁里又加锁。

注意:本文仅代表个人理解和看法哟!和本人所在公司和团体无任何关系!

原文地址:https://www.cnblogs.com/wy697495/p/9757982.html

时间: 2024-11-03 05:32:30

java中多线程产生死锁的原因以及解决意见的相关文章

Java 中多线程

很多核心Java面试题来源于多线程(Multi-Threading)和集合框架(Collections Framework),理解核心线程概念时,娴熟的实际经验是必需的.这篇文章收集了 Java 线程方面一些典型的问题,这些问题经常被高级工程师所问到. 0.Java 中多线程同步是什么? 在多线程程序下,同步能控制对共享资源的访问.如果没有同步,当一个 Java 线程在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误的结果. 1.解释实现多线程的几种方法? 一

java中多线程模拟(多生产,多消费,Lock实现同步锁,替代synchronized同步代码块)

import java.util.concurrent.locks.*; class DuckMsg{ int size;//烤鸭的大小 String id;//烤鸭的厂家和标号 DuckMsg(){ } DuckMsg(int size, String id){ this.size=size; this.id=id; } public String toString(){ return id + " 大小为:" + size; } } class Duck{ private int

java中多线程的实例代码

今天开始学习java中的多线程,在看书的过程中写了一个实例来练习多线程的用法,下面把代码放到博文里,里面很多的注释,可以帮助理解.     运行结果如下: main:启动MessageLoop线程...main:等待MessageLoop线程结束...main:继续等待.main:继续等待.main:继续等待.main:继续等待.Thread-0:消息1main:继续等待.main:继续等待.main:继续等待.main:继续等待.Thread-0:消息2main:继续等待.main:继续等待.

关于java中文件删除失败的原因分析

最近在做一个文档管理系统,结果在删除文件的时候,一直提示我文件删除失败,当然啦,是我在jsp里面写的一个alert("文件删除失败!"),然后我就纳闷儿了,为什么删不掉呢?后来打开windows,找到相应的文件,用管理员权限去删除也删不掉!然后就给我报错,java TM...正在使用这个文件,我顿时就凌乱了,因为我使用的是MyEcplise,所以我又回去检查代码,后来终于找到元凶了,是一个警告导致的错误!警告啊!下面我贴上代码: /** * 获取单个文件的大小 * @param fil

java中多线程通信实例:生产者消费者模式

线程间的通信: 其实就是多个线程再操作同一个资源,但是操作的动作不同   当某个线程进入synchronized块后,共享数据的状态不一定满足该线程的需要,需要其他线程改变共享数据的状态后才能运行,而由于当时线程对共享资源时独占的,它必须解除对共享资源的锁定的状态,通知其他线程可以使用该共享资源. Java中的 wait(),notify(),notifyAll()可以实现线程间的通信. 生产者--消费者问题是典型的线程同步和通信问题 /** * 生产者和消费者问题,生产者生成出产品,消费者去购

Android开发中java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx}: java.lang.NullPointerException 错误的集中原因及解决办法

上面那个问题刚解决,就又来一个问题~~~~ 错误信息字符串:java.lang.RuntimeException: Unable to start activity ComponentInfo{...}: java.lang.NullPointerException 网上有不少解决办法 一般都会在Activity  onCreate()方法里的setContentView(XXX)发生此错误,网上查阅了很多原因,大概有四种重要可能的原因: 原因一:xxx的错误,若为R.layout.main 

java中多线程执行时,为何调用的是start()方法而不是run()方法

Thead类中start()方法和run()方法的区别 1,start()用来启动一个线程,当调用start()方法时,系统才会开启一个线程,通过Thead类中start()方法来启动的线程处于就绪状态(可运行状态),此时并没有运行,一旦得到CPU时间片,就自动开始执行run()方法.此时不需要等待run()方法执行完也可以继续执行下面的代码,所以也由此看出run()方法并没有实现多线程. 2,run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的.如果直接调用run(),其实就相当

SQL Server死锁产生原因及解决办法

SQL Server死锁产生原因及解决办法 2006-07-18 05:12:10 分类: SQL Server 其实所有的死锁最深层的原因就是一个:资源竞争 表现一: 一个用户A 访问表A(锁住了表A),然后又访问表B,另一个用户B 访问表B(锁住了表B),然后企图访问表A,这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了,同样用户B要等用户A释放表A才能继续这就死锁了. 解决方法: 这种死锁是由于你的程序的BUG产生的,除了调整你的程序

用adb pull命令从android系统中读取文件失败的原因及解决办法

问题:使用adb pull命令从android系统中读取文件失败.显示:Permission denied 原因:是由于文件权限原因引起. 使用ls -l命令查看android系统中的文件权限为: -rw-rw---- app_51   app_51 也就是说,该文件只有app_51用户以及app_51群组拥有读写权限,而adb shell的用户为shell,既不是app_51用户,也不在app_51群组中,所以没有权限读取这个文件.所以就出现了 Permission denied. 解决方法: