Java中的死锁:何时发生?

时间:2010-08-30 08:09:48

标签: java java-me synchronization deadlock

我正在为J2ME创建一个应用程序它会完全冻结 并且需要很长时间让AMS关闭它。在我看来,这似乎是一个死锁问题。

你能告诉我什么可能导致死锁吗?例如,调用对象的同步方法会导致死锁,如果它调用自己的另一个同步方法吗?

谢谢!


更新

我是否正确地说在下列情况下应该发生僵局:

对象 P 调用对象 A 的同步方法,该方法调用对象 B 的同步方法,该方法调用对象的synch方法的 A

对不起,如果它看起来很愚蠢,很可能就是这样。但这就是为什么我在问。谢谢!

4 个答案:

答案 0 :(得分:12)

  例如,调用对象的synchronized方法会导致死锁,如果它调用自己的另一个同步方法吗?

不,因为Java中的synchronized锁是可重入:您可以多次从同一个线程获取相同的锁而没有问题。

发生死锁,例如当线程A持有锁L并尝试获取锁M时,线程B持有锁M并尝试获取锁L.因此两个线程都在等待另一个持有的锁,并且无法继续释放它们自己的锁。这导致两个线程永远等待。这种情况也可能涉及两个以上的线程。

死锁可能非常难以检测,因此典型的方法是通过精心设计来尝试避免它们。实现此目的的最简单方法是确保获取多个锁的任何线程始终以相同的预定义全局顺序获取它们。例如。如果在上面的例子中,线程A和B都先尝试获取锁L,那么锁M,就不会有死锁。

您的问题可能是由其他问题引起的,也可能是死锁引起的,例如:一个 livelock (当一个线程没有被阻塞时,仍然无法继续进行,因为它一直在重试一个总是失败的操作)。

更新:从对象外部访问锁

使用Java内部锁(即synchronized块),底层Lock对象本身在代码中不可见,只有我们锁定的对象。考虑

class MyClass {
  private Object object = new Object();

  public synchronized void synchronizedOnThis1() {
    ...
  }
  public void synchronizedOnThis2() {
    synchronized(this) {
      ...
    }
  }
  public void synchronizedOnPrivateObject() {
    synchronized(object) {
      ...
    }
  }
}

class ExternalParty {
  public void messUpLocks() {
    final MyClass myObject = new MyClass();
    synchronized(myObject) {
      Thread otherThread = new Thread() {
        public void run() {
            myObject.synchronizedOnThis1();
        }
      };
      otherThread.start();
      // do a lengthy calculation - this will block the other thread
    }
  }
}

synchronizedOnThis*方法在包含MyClass实例上同步;这两种方法的同步是等价的。但是,外部世界显然可以访问类实例,因此外部方可以将其用作类外部的锁,如上所示。如果该对象可从另一个线程访问,并且该线程调用其synchronizedOnThis*方法之一,则只要此线程位于synchronized(myObject)块内,该调用就会阻塞。

OTOH方法synchronizedOnPrivateObject使用私有对象作为锁。如果该对象没有以任何方式发布给外部各方,则其他任何人都无法(无意中或恶意地)导致涉及此锁定的死锁。

答案 1 :(得分:2)

最可能的原因是两个线程试图获取两个对象的锁。线程1锁定A并且正在等待B,但是线程2锁定B并且正在等待A.两个线程最终都会等待永远不会被释放的对象。

除了确保您的代码以非常明确的顺序执行操作之外,没有快速解决方案。作为一般规则,同步块应尽可能小,通常在特定对象上而不是在方法上。进行大量登录,看看是否可以找出这样的事情是否正在发生。

Java 5具有显式的Lock对象,允许更精细的控制,包括超过简单同步块的超时,但我不知道它们是否对J2ME有用。有一个Java 5并发库的后端端口,如果这个问题足够大,值得使用它,就有可能使用J2ME - http://backport-jsr166.sourceforge.net/

答案 2 :(得分:1)

答案 3 :(得分:0)

  

什么时候发生死锁?

过度使用同步锁定顺序不一致会导致死锁。

  

避免死锁的解决方案

维护订单并减少使用同步。

以下是导致死锁的一种情况。

    public void method1() {
            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");

                synchronized (Integer.class) {
                    System.out.println("Aquired lock on Integer.class object");
                }
            }
        }

        public void method2() {
            synchronized (Integer.class) {
                System.out.println("Aquired lock on Integer.class object");

                synchronized (String.class) {
                    System.out.println("Aquired lock on String.class object");
                }
            }
        }

如果两个或多个线程都调用method1()和method2(),则很有可能发生死锁,因为如果thead 1在执行method1()时获取对Sting对象的锁定,则线程2获取对Integer对象的锁定执行method2()时,两个都将等待彼此释放对Integer和String的锁定以继续进行,这将永远不会发生。

如果您仔细查看了上面的代码,您可能已经发现死锁的真正原因不是多线程,而是它们访问锁定的方式,如果您提供有序访问,那么问题将得到解决,这是固定版本。< / p>

public void method1() {
    synchronized (Integer.class) {
        System.out.println("Aquired lock on Integer.class object");

        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");
        }
    }
}

public void method2() {
    synchronized (Integer.class) {
        System.out.println("Aquired lock on Integer.class object");

        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");
        }
    }
}

现在不存在任何死锁,因为两个方法都以相同的顺序访问Integer和String对象的锁。因此,如果线程A获取对Integer对象的锁定,则线程B将不会继续,直到线程A释放整数锁定,同样,即使线程B保持字符串锁定,线程A也不会被阻止,因为现在线程B不会期望线程A释放整数锁定继续前进。

Courtesy