精华内容
下载资源
问答
  • 本篇文章主要介绍了Java线程死锁的出现和解决方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Java线程死锁解决方案

    千次阅读 2016-09-24 00:13:57
    要了解线程死锁,首先要明白什么是死锁 死锁 通俗点讲:死锁就是两个或两个以上的进程或线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。 ...

    要了解线程死锁,首先要明白什么是死锁


    死锁

    通俗点讲:死锁就是两个或两个以上的进程或线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。


    用简单一点的例子来说吧


    比如这个交通堵塞的例子,从图中可以看到四个方向行驶的汽车互相阻塞,如果没有任何一个方向的汽车退回去,那么将形成一个死锁

    上述图中有产生死锁的四个原因:

    1.互斥条件:一个资源每次只能被一个线程使用。图上每条路上只能让一个方向的汽车通过,故满足产生死锁的条件之一

    2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。可以看出,图上每个方向的汽车都在等待其他方向的汽车撤走,故满足产生死锁的条件之二

    3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。这里假设没有交警,那么没有人能强行要求其他方向上的汽车撤离,故满足产生死锁的条件之三

    4.循环等待条件:若干进程或线程之间形成一种头尾相接的循环等待资源关系。这个在图中很直观地表达出来了


    死锁Java代码小例子

    package huaxin2016_9_9;
    
    public class ThreadDeadlock {
    	public static void main(String[] args) throws InterruptedException {
    		Object obj1 = new Object();
    		Object obj2 = new Object();
    		Object obj3 = new Object(); 
    		//新建三个线程
    		Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1"); 
    		Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2"); 
    		Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3"); 
    		//让线程依次开始
    		t1.start(); 
    		//让线程休眠
    		Thread.sleep(5000);
    		t2.start();
    		Thread.sleep(5000);
    		t3.start();
    	} 
    } 
    class SyncThread implements Runnable{ 
    	private Object obj1; 
    	private Object obj2;
    	//构造函数
    	public SyncThread(Object o1, Object o2){ 
    		this.obj1=o1;
    		this.obj2=o2;
    	} 
    	@Override
    	public void run() {
    		//获取并当前运行线程的名称
    		String name = Thread.currentThread().getName();
    		System.out.println(name + " acquiring lock on "+obj1);
    		
    		
    		synchronized (obj1) { 
    			System.out.println(name + " acquired lock on "+obj1); 
    			work();
    			System.out.println(name + " acquiring lock on "+obj2); 
    			synchronized (obj2) { 
    				System.out.println(name + " acquired lock on "+obj2); 
    				work();
    		    } 
    			System.out.println(name + " released lock on "+obj2); 
    	    } 
    		System.out.println(name + " released lock on "+obj1); 
    		System.out.println(name + " finished execution.");
    	}
    	private void work() { 
    		try { 
    			Thread.sleep(30000); 
    		} 
    		catch (InterruptedException e) { 
    			e.printStackTrace();
    		}
    	} 
    }
    
    上述死锁小例子运行结果为

    t1 acquiring lock on java.lang.Object@675d5ed4
    t1 acquired lock on java.lang.Object@675d5ed4
    t2 acquiring lock on java.lang.Object@7943f708
    t2 acquired lock on java.lang.Object@7943f708
    t3 acquiring lock on java.lang.Object@46767615
    t3 acquired lock on java.lang.Object@46767615
    t1 acquiring lock on java.lang.Object@7943f708
    t2 acquiring lock on java.lang.Object@46767615
    t3 acquiring lock on java.lang.Object@675d5ed4
    可以很直观地看到,t1、t2、t3都在要求资源,却都保持自己的资源,故而引起死锁


    解决方案:

    1.打破互斥条件,我们需要允许进程同时访问某些资源,这种方法受制于实际场景,不太容易实现条件;

    2.打破不可抢占条件,这样需要允许进程强行从占有者那里夺取某些资源,或者简单一点理解,占有资源的进程不能再申请占有其他资源,必须释放手上的资源之后才能发起申请,这个其实也很难找到适用场景;

    3. 进程在运行前申请得到所有的资源,否则该进程不能进入准备执行状态。这个方法看似有点用处,但是它的缺点是可能导致资源利用率和进程并发性降低

    4.  避免出现资源申请环路,即对资源事先分类编号,按号分配。这种方式可以有效提高资源的利用率和系统吞吐量,但是增加了系统开销,增大了进程对资源的占用时间。


    (1). 最简单、最常用的方法就是进行系统的重新启动,不过这种方法代价很大,它意味着在这之前所有的进程已经完成的计算工作都将付之东流,包括参与死锁的那些进程,以及未参与死锁的进程;

    (2). 撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。这时又分两种情况:一次性撤消参与死锁的全部进程,剥夺全部资源;或者逐步撤消参与死锁的进程,逐步收回死锁进程占有的资源。一般来说,选择逐步撤消的进程时要按照一定的原则进行,目的是撤消那些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程运行时的代价和与此进程相关的外部作业的代价等因素;

    (3). 进程回退策略,即让参与死锁的进程回退到没有发生死锁前某一点处,并由此点处继续执行,以求再次执行时不再发生死锁。虽然这是个较理想的办法,但是操作起来系统开销极大,要有堆栈这样的机构记录进程的每一步变化,以便今后的回退,有时这是无法做到的。




    展开全文
  • Java线程死锁需要如何解决

    千次阅读 2016-12-07 17:59:10
    Java线程死锁如何避免这一悲剧 Java线程死锁需要如何解决,这个问题一直在我们不断的使用中需要只有不断的关键。不幸的是,使用上锁会带来其他问题。让我们来看一些常见问题以及相应的解决方法:  Java线程死锁 ...

    Java线程死锁如何避免这一悲剧  Java线程死锁需要如何解决,这个问题一直在我们不断的使用中需要只有不断的关键。不幸的是,使用上锁会带来其他问题。让我们来看一些常见问题以及相应的解决方法:

      Java线程死锁

      Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。

      假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。这只是人为设计的例子,但尽管在运行时很难探测到,这类情况却时常发生。虽然要探测或推敲各种情况是非常困难的,但只要按照下面几条规则去设计系统,就能够避免Java线程死锁问题:

      让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。

     

      将多个锁组成一组并放到同一个锁下。前面Java线程死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。

     

      将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判断是否整个银器集合中的对象锁都可获得。如果是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。

     

      最重要的是,在编写代码前认真仔细地设计整个系统。多线程是困难的,在开始编程之前详细设计系统能够帮助你避免难以发现Java线程死锁的问题。

     

      Volatile 变量,volatile 关键字是 Java 语言为优化编译器设计的。以下面的代码为例:

      1.classVolatileTest {

      2.publicvoid foo() {

      3.booleanflag = false;

      4.if(flag){

      5.//thiscould happen

      6.}

      7.}

      8.}

      一个优化的编译器可能会判断出if部分的语句永远不会被执行,就根本不会编译这部分的代码。如果这个类被多线程访问, flag被前面某个线程设置之后,在它被if语句测试之前,可以被其他线程重新设置。用volatile关键字来声明变量,就可以告诉编译器在编译的时候,不需要通过预测变量值来优化这部分的代码。

      无法访问的Java线程死锁有时候虽然获取对象锁没有问题,线程依然有可能进入阻塞状态。在 Java 编程中IO就是这类问题最好的例子。当线程因为对象内的IO调用而阻塞时,此对象应当仍能被其他线程访问。该对象通常有责任取消这个阻塞的IO操作。造成阻塞调用的线程常常会令同步任务失败。如果该对象的其他方法也是同步的,当线程被阻塞时,此对象也就相当于被冷冻住了。

      其他的线程由于不能获得对象的Java线程死锁,就不能给此对象发消息(例如,取消 IO 操作)。必须确保不在同步代码中包含那些阻塞调用,或确认在一个用同步阻塞代码的对象中存在非同步方法。尽管这种方法需要花费一些注意力来保证结果代码安全运行,但它允许在拥有对象的线程发生阻塞后,该对象仍能够响应其他线程。

     

    =======================================================================

    死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 

      导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性的访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。 

      由于这个原因,在使用“synchronized”关键词时,很容易出现两个线程互相等待对方做出某个动作的情形。代码一是一个导致死锁的简单例子。 

    //代码一

    Java代码 

    1.  class Deadlocker {  

    2.   int field_1;  

    3.   private Object lock_1 = new int[1];   

    4.   int field_2;  

    5.   private Object lock_2 = new int[1];   

    6.    

    7.   public void method1(int value) {  

    8.    “synchronized” (lock_1) {  

    9.     “synchronized” (lock_2) {  

    10.     field_1 = 0; field_2 = 0;   

    11.    }  

    12.   }  

    13.  }  

    14.   

    15.  public void method2(int value) {  

    16.   “synchronized” (lock_2) {  

    17.    “synchronized” (lock_1) {  

    18.     field_1 = 0; field_2 = 0;   

    19.    }  

    20.   }  

    21.  }  

    22. }   

    23.    

    Java代码  

    1.  class Deadlocker {  

    2.   int field_1;  

    3.   private Object lock_1 = new int[1];  

    4.   int field_2;  

    5.   private Object lock_2 = new int[1];  

    6.    

    7.   public void method1(int value) {  

    8.    “synchronized” (lock_1) {  

    9.     “synchronized” (lock_2) {  

    10.     field_1 = 0; field_2 = 0;  

    11.    }  

    12.   }  

    13.  }  

    14.   

    15.  public void method2(int value) {  

    16.   “synchronized” (lock_2) {  

    17.    “synchronized” (lock_1) {  

    18.     field_1 = 0; field_2 = 0;  

    19.    }  

    20.   }  

    21.  }  

    22. }  

    23.    



      参考代码一,考虑下面的过程: 

      ◆ 一个线程(ThreadA)调用method1()。 

      ◆ ThreadA在lock_1上同步,但允许被抢先执行。 

      ◆ 另一个线程(ThreadB)开始执行。 

      ◆ ThreadB调用method2()。 

      ◆ ThreadB获得lock_2,继续执行,企图获得lock_1。但ThreadB不能获得lock_1,因为ThreadA占有lock_1。 

      ◆ 现在,ThreadB阻塞,因为它在等待ThreadA释放lock_1。 

      ◆ 现在轮到ThreadA继续执行。ThreadA试图获得lock_2,但不能成功,因为lock_2已经被ThreadB占有了。 

      ◆ ThreadA和ThreadB都被阻塞,程序死锁。 

      当然,大多数的死锁不会这么显而易见,需要仔细分析代码才能看出,对于规模较大的多线程程序来说尤其如此。好的线程分析工具,例如JProbeThreadalyzer能够分析死锁并指出产生问题的代码位置。 

      隐性死锁 

      隐性死锁由于不规范的编程方式引起,但不一定每次测试运行时都会出现程序死锁的情形。由于这个原因,一些隐性死锁可能要到应用正式发布之后才会被发现,因此它的危害性比普通死锁更大。下面介绍两种导致隐性死锁的情况:加锁次序和占有并等待。 

      加锁次序 

      当多个并发的线程分别试图同时占有两个锁时,会出现加锁次序冲突的情形。如果一个线程占有了另一个线程必需的锁,就有可能出现死锁。考虑下面的情形,ThreadA和ThreadB两个线程分别需要同时拥有lock_1、lock_2两个锁,加锁过程可能如下: 

      ◆ ThreadA获得lock_1; 

      ◆ ThreadA被抢占,VM调度程序转到ThreadB; 

      ◆ ThreadB获得lock_2; 

      ◆ ThreadB被抢占,VM调度程序转到ThreadA; 

      ◆ ThreadA试图获得lock_2,但lock_2被ThreadB占有,所以ThreadA阻塞; 

      ◆ 调度程序转到ThreadB; 

      ◆ ThreadB试图获得lock_1,但lock_1被ThreadA占有,所以ThreadB阻塞; 

      ◆ ThreadA和ThreadB死锁。 

      必须指出的是,在代码丝毫不做变动的情况下,有些时候上述死锁过程不会出现,VM调度程序可能让其中一个线程同时获得lock_1和lock_2两个锁,即线程获取两个锁的过程没有被中断。在这种情形下,常规的死锁检测很难确定错误所在。 

      占有并等待 

      如果一个线程获得了一个锁之后还要等待来自另一个线程的通知,可能出现另一种隐性死锁,考虑代码二。 

    //代码二

    Java代码 

    1.  public class queue {  

    2.   static java.lang.Object queueLock_;  

    3.   Producer producer_;  

    4.   Consumer consumer_;  

    5.    

    6.   public class Producer {  

    7.    void produce() {  

    8.     while (!done) {  

    9.      “synchronized” (queueLock_) {  

    10.      produceItemAndAddItToQueue();  

    11.      “synchronized” (consumer_) {  

    12.       consumer_.notify();  

    13.      }  

    14.     }  

    15.    }  

    16.   }  

    17.   

    18.   public class Consumer {  

    19.    consume() {  

    20.     while (!done) {  

    21.      “synchronized” (queueLock_) {  

    22.       “synchronized” (consumer_) {  

    23.        consumer_.wait();  

    24.       }  

    25.       removeItemFromQueueAndProcessIt();  

    26.      }  

    27.     }  

    28.    }  

    29.   }  

    30.  }  

    31. }   

    32.    

    Java代码  

    1.  public class queue {  

    2.   static java.lang.Object queueLock_;  

    3.   Producer producer_;  

    4.   Consumer consumer_;  

    5.    

    6.   public class Producer {  

    7.    void produce() {  

    8.     while (!done) {  

    9.      “synchronized” (queueLock_) {  

    10.      produceItemAndAddItToQueue();  

    11.      “synchronized” (consumer_) {  

    12.       consumer_.notify();  

    13.      }  

    14.     }  

    15.    }  

    16.   }  

    17.   

    18.   public class Consumer {  

    19.    consume() {  

    20.     while (!done) {  

    21.      “synchronized” (queueLock_) {  

    22.       “synchronized” (consumer_) {  

    23.        consumer_.wait();  

    24.       }  

    25.       removeItemFromQueueAndProcessIt();  

    26.      }  

    27.     }  

    28.    }  

    29.   }  

    30.  }  

    31. }  

    32.    




      在代码二中,Producer向队列加入一项新的内容后通知Consumer,以便它处理新的内容。问题在于,Consumer可能保持加在队列上的锁,阻止Producer访问队列,甚至在Consumer等待Producer的通知时也会继续保持锁。这样,由于Producer不能向队列添加新的内容,而Consumer却在等待Producer加入新内容的通知,结果就导致了死锁。 

      在等待时占有的锁是一种隐性的死锁,这是因为事情可能按照比较理想的情况发展—Producer线程不需要被Consumer占据的锁。尽管如此,除非有绝对可靠的理由肯定Producer线程永远不需要该锁,否则这种编程方式仍是不安全的。有时“占有并等待”还可能引发一连串的线程等待,例如,线程A占有线程B需要的锁并等待,而线程B又占有线程C需要的锁并等待等。 

      要改正代码二的错误,只需修改Consumer类,把wait()移出“synchronized”()即可。 

      因此避免死锁的一个通用的经验法则是:当几个线程都要访问共享资源A、B、C时,保证使每个线程都按照同样的顺序去访问它们,比如都先访问A,在访问B和C。 
      此外,Thread类的suspend()方法也很容易导致死锁,因此这个方法已经被废弃了


    展开全文
  • 主要介绍了Java线程死锁实例及解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了 Java 线程死锁的问题解决办法的相关资料,希望通过本大家能帮助到大家,遇到类似问题能够解决,需要的朋友可以参考下
  • Java解决线程死锁三种方案

    万次阅读 2018-11-30 16:41:13
    死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞状态。 下面代码展示...

    什么是死锁?

    死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞状态。

    下面代码展示一下死锁

    package com.zhr.Thread;
    
    /**
     * @ Author     :zhenghaoran.
     * @ Date       :Created in 14:55 2018/11/30
     * @ Description:
     */
    public class DeadLockSample extends Thread{
        private String first;
        private String second;
        public DeadLockSample (String name,String first,String second){
            super(name);
            this.first = first;
            this.second = second;
        }
    
        public void run(){
            synchronized (first){
                System.out.println(this.getName() + " obtained:" + first);
    
                try {
                    Thread.sleep(1000L);
                    synchronized (second){
                        System.out.println(this.getName() +" obtained: " + second);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            String lockA = "lockA";
            String lockB = "lockB";
            DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
            DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
    
        }
    
    }
    结果:
    Thread1 obtained:lockA
    Thread2 obtained:lockB
    或者
    Thread2 obtained:lockB
    Thread1 obtained:lockA
    

    看了上面的代码大家可能会有疑问在代码中明明是先启动的Thread1线程啊,为什么还会有第二个结果变成了Threa2先运行,其实这是在操作系统层面中系统会进行线程调度,在Java中线程调度是抢占式的,主线程虽然先运行了Thread1.start(),但是并不代表就一定会先运行run()方法,在两个线程没有设置线程优先级的时候默认都是5级,现在分别启动线程1核线程2,此时操作系统会将处在就绪状态的变为运行运行状态,所以这个时候线程2要是先就绪,并且cpu从就绪的线程当中选择了线程2name就会线程2先执行。

    当然在线上如果写出了死锁我们应该怎么办呢?别急让我来给你介绍两个工具都是 JDK自带的 jstack 和 jConsole。下面演示jstack的用法

    • 首先运行刚才的程序,让其处于死锁的状态
    • 运行ps -ef | grep java 找到正在运行的java程序的pid
    • 然后进入到JAVA_HOME/bin下运行 ./jstack 10586(这个是我当前运行的pid)

    如下图:
    在这里插入图片描述
    可以看到Threa2正在处于BLOCKED的状态,而Thread1也是同样处于BLOCKED这就说明了发生了死锁。并且可以定位问题到在是在DeadLockSimple.java:24这个文件的第24行,怎么样很神奇吧。

    下面我们看一下使用jConsole如何检测运行的代码是否有死锁

    • 同样的操作步骤 运行程序
    • 在JAVA_HOME/bin 目录下 运行jConsole 选择到对应的PID 可以看到对应的类名。

    如下图
    在这里插入图片描述
    然后我们选择死锁就能看到了当前死锁的线程
    在这里插入图片描述

    还有重头戏也就是最牛的 在代码中就可以进行检测,定位死锁,使用Java提供的标准管理 API,ThreadMXBean 类,具体代码如下

    package com.zhr.Thread;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.ThreadInfo;
    import java.lang.management.ThreadMXBean;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @ Author     :zhenghaoran.
     * @ Date       :Created in 14:55 2018/11/30
     * @ Description:
     */
    public class DeadLockSample extends Thread{
        private String first;
        private String second;
        public DeadLockSample (String name,String first,String second){
            super(name);
            this.first = first;
            this.second = second;
        }
    
        public void run(){
            synchronized (first){
                System.out.println(this.getName() + " obtained:" + first);
    
                try {
                    Thread.sleep(1000L);
                    synchronized (second){
                        System.out.println(this.getName() +" obtained: " + second);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    
            Runnable dlCheck = new Runnable() {
                public void run() {
                    long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
                    if(deadlockedThreads != null){
                        ThreadInfo[] threadInfos= threadMXBean.getThreadInfo(deadlockedThreads);
                        System.out.println("Detected deadlock threads:");
                        for (ThreadInfo threadInfo : threadInfos) {
                            System.out.println(threadInfo.getThreadName());
    
                        }
                    }
    
                }
            };
    
            ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
            //稍等 1 秒,然后每 2 秒进行一次死锁扫描
            scheduler.scheduleAtFixedRate(dlCheck,1L,2L, TimeUnit.SECONDS);
    
            String lockA = "lockA";
            String lockB = "lockB";
            DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
            DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
        }
    
    }
    
    
    展开全文
  • java线程死锁解决方法

    千次阅读 2018-07-28 14:17:34
    1.线程同步 多线程引发的安全问题 一个非常经典的案例,银行取钱的问题。假如你有一张银行卡,里面有5000块钱,然后你去银行取款2000块钱。正在你取钱的时候,取款机正要从你的5000余额中减去2000的时候,你的老婆...

    1.线程同步

    多线程引发的安全问题

    一个非常经典的案例,银行取钱的问题。假如你有一张银行卡,里面有5000块钱,然后你去银行取款2000块钱。正在你取钱的时候,取款机正要从你的5000余额中减去2000的时候,你的老婆正巧也在用银行卡对应的存折取钱,由于取款机还没有把你的2000块钱扣除,银行查到存折里的余额还剩5000块钱,准备减去2000。这时,有趣的事情发生了,你和你的老婆从同一个账户共取走了4000元,但是账户最后还剩下3000元。

    使用代码模拟下取款过程:

    public class ThreadTest {
    
        public static void main(String[] args) {
            // 创建一个账户,里面有存款5000元
            Account account = new Account(5000);
            // 模拟取钱过程
            GetMoney getMoney = new GetMoney(account);
            new Thread(getMoney, "你").start();
            new Thread(getMoney, "你老婆").start();
        }
    }
    
    class GetMoney implements Runnable {
    
        private Account account;
    
        public GetMoney(Account account) {
            super();
            this.account = account;
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "账户现在有"
                    + account.getMoney() + "元");
            // 使效果更明显,休眠10ms
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int money = account.getMoney() - 2000;
            account.setMoney(money);
            System.out.println(Thread.currentThread().getName() + "取了2000元,账户现在有"
                    + account.getMoney() + "元");
        }
    
    }
    
    class Account {
        private int money;
    
        public Account(int money) {
            super();
            this.money = money;
        }
    
        public int getMoney() {
            return money;
        }
    
        public void setMoney(int money) {
            this.money = money;
        }
    }

    看下打印信息:

    你账户现在有5000元
    你老婆账户现在有5000元
    你取了2000元,账户现在有3000元
    你老婆取了2000元,账户现在有3000元

    从上面的案例可以看出,当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。 
    Java中可以使用synchronized关键字来取得一个对象的同步锁。

    // Object可以为任何对象,表示当前线程取得该对象的锁。
    synchronized (Object) {
    }
    •  

    修改一下上面的案例,在run方法中加入同步锁:

    @Override
    public void run() {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + "账户现在有"
                    + account.getMoney() + "元");
            // 使效果更明显,休眠10ms
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int money = account.getMoney() - 2000;
            account.setMoney(money);
            System.out.println(Thread.currentThread().getName()
                    + "取了2000元,账户现在有" + account.getMoney() + "元");
        }
    }

    看下打印信息:

    你账户现在有5000元
    你取了2000元,账户现在有3000元
    你老婆账户现在有3000元
    你老婆取了2000元,账户现在有1000元

    当你取钱的时候,取款机锁定了你的账户,不允许其他人对账户进行操作,当你取完钱后,取款机释放了你的账户,你的老婆才可以取钱。

    2.死锁

    同步锁虽好,但也要科学使用,不然就会发生死锁,何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。 
    举个栗子,两个人面对面过独木桥,甲和乙都已经在桥上走了一段距离,即占用了桥的资源,甲如果想通过独木桥的话,乙必须退出桥面让出桥的资源,让甲通过,但是乙不服,为什么让我先退出去,我还想先过去呢,于是就僵持不下,导致谁也过不了桥,这就是死锁。

    下面用一段简单的代码来模拟死锁:

    public class DeadlockTest {
    
        public static void main(String[] args) {
            String str1 = new String("资源1");
            String str2 = new String("资源2");
    
            new Thread(new Lock(str1, str2), "线程1").start();
            new Thread(new Lock(str2, str1), "线程2").start();
        }
    }
    
    class Lock implements Runnable {
    
        private String str1;
        private String str2;
    
        public Lock(String str1, String str2) {
            super();
            this.str1 = str1;
            this.str2 = str2;
        }
    
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + "运行");
                synchronized (str1) {
                    System.out.println(Thread.currentThread().getName() + "锁住"
                            + str1);
                    Thread.sleep(1000);
                    synchronized (str2) {
                        // 执行不到这里
                        System.out.println(Thread.currentThread().getName()
                                + "锁住" + str2);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    看下打印信息:

    线程1运行
    线程2运行
    线程1锁住资源1
    线程2锁住资源2

    第一个线程锁住了资源1(甲占有桥的一部分资源),第二个线程锁住了资源2(乙占有桥的一部分资源),线程1企图锁住资源2(甲让乙退出桥面,乙不从),进入阻塞,线程2企图锁住资源1(乙让甲退出桥面,甲不从),进入阻塞,死锁了。

    .解决死锁方法:同一个方法只能被一个线程使用,不要嵌套锁。独木桥每次只能通过一个人。

     

    展开全文
  • Java线程死锁案例

    千次阅读 2019-06-28 21:28:19
    就是,两个线程互相等待着对象释放锁,一直这样僵持下去,所以导致了死锁的产生,下面这个代码就会产生一个死锁: package com.example.demo.thread; public class DeadLockDemo { private static String A = "A";...
  • 所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。 死锁产生的原因 系统资源的竞争 进程推进顺序非法进程在运行过程中,请求和释放资源的顺序不当,也同样...
  • 主要介绍了JVM---jstack分析Java线程CPU占用,线程死锁解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 如何避免Java死锁? 是Java面试中最受欢迎的问题之一,也是本季多线程的风格,主要是在高层提出,并带有很多后续问题。 尽管问题看起来很基础,但是一旦您开始深入研究,大多数Java开发人员就会陷入困境。 面试...
  • Java线程死锁

    2019-03-14 11:56:44
    不同的线程分别占用对方的同步资源不放弃,都在等待对方放弃自己需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程死锁。 补充 在数据库系统的设计中考虑了监测死锁以及从死锁中恢复,...
  • 经典的java线程死锁例子

    万次阅读 2018-05-01 23:11:36
    一、多个锁之间的嵌套产生死锁。 public class DaneLock { public static void main(String[] args) { DieLock d1=new DieLock(true); DieLock d2=new DieLock(false); Thread t1=new Thread(d1); ...
  • @T拯救Java线程死锁问题!最强解决方案看这里!OC 众所周知,在Java 语言中每个Object都有一个隐含的锁,通过该锁,我们可以使用synchronized 关键字来保证代码块的原子性。synchronized 能够使线程在执行到该代码块...
  • 死锁的概念 所谓死锁,是指多个进程在运行过程中因争夺资源而照成的一种僵局。当进程处于这种僵持状态时,若无外力作用,它们都将无法再继续执行。 产生死锁的原因 竞争资源。当系统中供多个进程共享的资源如...
  • Java线程死锁查看分析方法 如何查看是否有Java线程死锁?下面介绍两种方法。 一.Jconsole  Jconsole是JDK自带的图形化界面工具,使用JDK给我们的的工具JConsole,可以通过打开cmd然后输入jconsole打开。  连接...
  • java 死锁 解决 JavaOne年度会议的一大优点是主题专家介绍了几个技术和故障排除实验室。 这些实验室之一尤其引起了我的关注:Java冠军Heinz Kabutz提出的“ HOL6500-查找和解决Java死锁 ”。 这是我在该主题上看到...
  • java7中 // hashmap put过程中产生扩容 java.util.HashMap#put java.util.HashMap#addEntry java.util.HashMap#resize java.util.HashMap#transfer // 移动原来的数组rehash后放到新数组 void transfer(Entry[] ...
  • java线程模拟死锁(代码)

    千次阅读 2019-07-05 12:04:09
    死锁是在多线程情况下最严重的问题,在多线程对公共资源(文件,数据)等进行操作时,彼此不释放自己的资源,而去试图操作其他线程的资源,而形成的交叉引用,就会产生死锁。 假如我有一个需求,有一个线程,先写入...
  • 死锁解决办法 1. 死锁是什么 死锁是指两个或两个以上的进程在执行过程中因争夺资而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去, 如果系统资源允是,进程的资源请求都能够得到满是,死锁出现的...
  • Java线程死锁如何避免这一悲剧 Java线程死锁需要如何解决,这个问题一直在我们不断的使用中需要只有不断的关键。不幸的是,使用上锁会带来其他问题。让我们来看一些常见问题以及相应的解决方法:  Java线程死锁 ...
  • 一、线程并发同步概念 线程同步其核心就在于一个“同”。所谓“同”就是协同、协助、配合,“同步”就是协同步调昨,也就是按照预定的先后顺序进行运行,即“你先,我等, 你做完,我再做”。 线程同步,就是当...
  • 其中的一个实验室今年特别吸引了我的注意力:“ HOL6500-查找和解决Java死锁 ”,由Java冠军Heinz Kabutz提出 。 这是我在该主题上看到的最好的演示之一。 我建议您自己下载,运行和研究实验室。 本文将重温这个...
  • java线程死锁解决方法解决方法:http://www.yayihouse.com/yayishuwu/chapter/1302
  • 死锁是怎么造成的?...情况一: 死锁是两个或多个线程互相等待对方所有用的资源情形:现在有线程1和线程2。线程1执行过程中,先锁定了对象a,然后需要再锁定b才能继续执行代码;而线程2正巧相反,先...
  • 线程死锁以及解决方法

    千次阅读 2017-06-09 23:11:44
    解决死锁没有简单的方法,这是因为线程产生死锁都各有各的原因,而且往往具有很高的负载。大多数软件测试产生不了足够多的负载,所以不可能暴露所有的线程错误。在这里中,下面将讨论开发过程常见的4类典型的死锁和...
  • 为了解决线程死锁问题,不是不使用锁,而是用信号量去控制。 信号量可以控制资源能被多少线程访问,此处指定只能被一个线程访问,就实现了锁的功能;然而,信号量可以指定获取的超时时间,所以,可以根据这个超时...
  • java线程死锁例子及解决方法

    千次阅读 2015-09-08 08:39:30
    Java线程死锁是由于有些资源彼此交叉取用,就有可能造成死结.如1线程中 取得A对象的锁定后又要取得B对象的锁定.但是同时2线程中取得B对象的锁定后又要取得A对象的锁定.这两个线程同时发生时就会造成,1线程拿到A对象...
  • 线程死锁解决办法

    千次阅读 2020-02-05 12:34:27
    所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。 死锁产生的必要条件 以下这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 83,792
精华内容 33,516
关键字:

java线程死锁怎么解决

java 订阅