Java 中的死锁:什么时候发生?

发布于 2024-09-16 15:34:18 字数 400 浏览 6 评论 0原文

我正在为 J2ME 开发一个应用程序,有时它会完全冻结,并且 AMS 需要相当长的时间来关闭它。在我看来,这就像一个僵局问题。

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

谢谢!


更新

我说的对吗,在以下情况下应该会发生死锁:

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

抱歉,如果我看起来很愚蠢,很可能确实如此。但这就是我问的原因。谢谢!

I am working on an app for the J2ME and sometime it freezes completely and it takes quite some time for the AMS to close it. It seems to me like a dead-lock issue.

Could you advise me as to what could cause dead locks? Would, for instance, calling a synchronized method of a object cause dead lock if it calls another synchronized method of its own?

Thanks!


Update

Am I right in saying that a deadlock is supposed to happen in the following case:

Object P calls a synch method of object A, that calls a synch method of object B, that calls a synch method of object A

Sorry if it looks stupid of me, most probably it is. But that's why I'm asking. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

海风掠过北极光 2024-09-23 15:34:18

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

不会,因为 Java 中的同步锁是可重入的:您可以从同一个线程多次获取同一个锁,不会出现问题。

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

死锁可能很难检测到,因此典型的方法是通过仔细设计来尝试避免死锁。实现此目的的最简单方法是确保任何要获取多个锁的线程始终以相同的预定义全局顺序获取它们。例如,如果在上面的示例中,线程 A 和 B 都尝试先获取锁 L,然后获取锁 M,则不会出现死锁。

您的问题也可能是由死锁以外的其他原因引起的,例如活锁(当一个线程虽然没有被阻止,但仍然无法进行时,因为它不断重试总是失败的操作)。

更新:从对象外部访问锁

使用Java内部锁(即同步块),底层的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 使用私有对象作为锁。如果该对象未以任何方式发布给外部各方,则任何其他人都不能(无意或恶意)导致涉及此锁的死锁。

Would, for instance, calling a synchronized method of a object cause dead lock if it calls another synchronized method of its own?

No, because synchronized locks in Java are reentrant: you can acquire the same lock from the same thread multiple times without a problem.

A deadlock occurs e.g. when thread A holds lock L and tries to acquire lock M, while thread B holds lock M and tries to acquire lock L. Thus both threads are waiting for a lock held by the other, and can not progress to release their own lock. This causes both threads to wait forever. The situation may be involving more than 2 threads as well.

Deadlocks can be very difficult to detect, so the typical way is to try to avoid them by careful design. The simplest means to achieve this is to ensure that any thread which is to acquire multiple locks, acquires them always in the same predefined global order. E.g. if in the above example both threads A and B attempt to acquire lock L first, then lock M, there will be no deadlock.

Your issue may be caused by other things than a deadlock too, e.g. a livelock (when a thread, while not blocked, still can't progress because it keeps retrying an operation that always fails).

Update: accessing the lock from outside the object

With Java intrinsic locks (i.e. synchronized blocks), the underlying Lock object itself is not visible in the code, only the object we are locking on. Consider

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
    }
  }
}

Both the synchronizedOnThis* methods are synchronized on the containing MyClass instance; the synchronization of the two methods is equivalent. However, the class instance is obviously accessible to the outside world, so an external party can use it as a lock from outside the class, as shown above. And if the object is accessible from another thread, and that thread calls one of its synchronizedOnThis* methods, that call will block as long as this thread is within the synchronized(myObject) block.

OTOH the method synchronizedOnPrivateObject uses a private object as lock. If that object is not published to external parties in any way, noone else can (inadvertently or malevolently) cause a deadlock involving this lock.

梦一生花开无言 2024-09-23 15:34:18

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

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

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

Most probable reason would be two threads trying to obtain locks to two objects. Thread 1 locks A and is waiting for B, but thread 2 locks B and is waiting for A. Both threads end up waiting forever for objects which will never be released.

There is no quick solution except make sure your code does things in a very well defined order. As a general rule, the synchronized block should be as small as possible, usually on a specific object than on a method. Put a lot of logging in and see if you can figure out if something like this is happening.

Java 5 has explicit Lock objects which allow a finer grained control including timeouts over simple synchronized blocks but I do not know if they would be useful for J2ME. There is a backport of the Java 5 concurrency libs which it may be possible to get working with J2ME - http://backport-jsr166.sourceforge.net/ if this issue is sufficiently large to merit using them.

从此见与不见 2024-09-23 15:34:18

什么时候会发生死锁?

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

避免死锁的解决方案

维护顺序,少用同步。

以下是一种导致死锁的场景。

    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() 都将被两个或多个线程调用,则很可能会出现死锁,因为如果线程 1 在执行 method1() 时获取 Sting 对象上的锁,而线程 2 在执行 method2 时获取 Integer 对象上的锁() 两者都将等待对方释放对 Integer 和 String 的锁定以进一步进行,但这永远不会发生。

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

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 释放 Integer 锁,同样,即使线程 B 持有 String 锁,线程 A 也不会被阻塞,因为现在线程 B 不会期望线程 A 释放 Integer 锁继续进行。

礼貌

when does deadlock occur?

Excessive usage of synchronization with inconsistent lock ordering causes deadlock.

Solution to avoid dead lock

Maintain the order and use less of synchronization.

The following is one scenario, which makes deadlock.

    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");
                }
            }
        }

If method1() and method2() both will be called by two or many threads , there is a good chance of deadlock because if thead 1 aquires lock on Sting object while executing method1() and thread 2 acquires lock on Integer object while executing method2() both will be waiting for each other to release lock on Integer and String to proceed further which will never happen.

if you have looked above code carefully you may have figured out that real reason for deadlock is not multiple threads but the way they access lock , if you provide an ordered access then problem will be resolved , here is the fixed version.

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");
        }
    }
}

Now there would not be any deadlock because both method is accessing lock on Integer and String object in same order . so if thread A acquires lock on Integer object , thread B will not proceed until thread A releases Integer lock , same way thread A will not be blocked even if thread B holds String lock because now thread B will not expect thread A to release Integer lock to proceed further.

Courtesy

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文