Java中的死锁:何时发生?
我正在为J2ME开发一个应用程序,有时它会完全冻结,AMS需要相当长的时间才能关闭它。在我看来,这似乎是一个死锁问题 你能告诉我什么会导致死锁吗?例如,如果调用对象的同步方法本身的另一个同步方法,调用该对象的同步方法是否会导致死锁 谢谢Java中的死锁:何时发生?,java,java-me,synchronization,deadlock,Java,Java Me,Synchronization,Deadlock,我正在为J2ME开发一个应用程序,有时它会完全冻结,AMS需要相当长的时间才能关闭它。在我看来,这似乎是一个死锁问题 你能告诉我什么会导致死锁吗?例如,如果调用对象的同步方法本身的另一个同步方法,调用该对象的同步方法是否会导致死锁 谢谢 更新 我所说的僵局应该在以下情况下发生,对吗 Objectp调用Objecta的同步方法,调用ObjectB的同步方法,调用Objecta的同步方法 对不起,如果我看起来很愚蠢,很可能是。但这就是我问的原因。谢谢 看看这个: 例如,如果调用对象的同步方法本身
更新 我所说的僵局应该在以下情况下发生,对吗 Objectp调用Objecta的同步方法,调用ObjectB的同步方法,调用Objecta的同步方法 对不起,如果我看起来很愚蠢,很可能是。但这就是我问的原因。谢谢 看看这个: 例如,如果调用对象的同步方法本身的另一个同步方法,调用该对象的同步方法是否会导致死锁 不,因为Java中的
同步的
锁是可重入的:您可以多次从同一线程获取相同的锁,而不会出现问题
例如,当线程A持有锁L并试图获取锁M,而线程B持有锁M并试图获取锁L时,就会发生死锁。因此,两个线程都在等待另一个线程持有的锁,并且无法继续释放自己的锁。这会导致两个线程永远等待。这种情况也可能涉及2个以上的线程
死锁可能很难检测,因此典型的方法是通过仔细设计来避免死锁。实现这一点的最简单方法是确保任何要获取多个锁的线程总是以相同的预定义全局顺序获取它们。例如,如果在上面的示例中,线程A和B都尝试先获取锁L,然后获取锁M,则不会出现死锁
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");
}
}
}
您的问题也可能是由死锁以外的其他原因引起的,例如livelock(当一个线程虽然没有被阻塞,但仍然无法进行,因为它不断重试一个总是失败的操作时)
更新:从对象外部访问锁
使用Java内部锁(即synchronized
块),底层锁
对象本身在代码中不可见,只有我们锁定的对象。考虑
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)
块中,该调用就会被阻止
o方法
synchronizedOnPrivateObject
使用私有对象作为锁。如果该对象未以任何方式发布给外部各方,则其他任何人都无法(无意或恶意)导致涉及该锁的死锁。最可能的原因是两个线程试图获取两个对象的锁。线程1锁定A并等待B,但线程2锁定B并等待A。两个线程最终都会永远等待永远不会释放的对象
除了确保您的代码按照定义良好的顺序执行操作外,没有快速的解决方案。一般来说,同步块应该尽可能小,通常在特定对象上比在方法上小。大量登录,看看你是否能弄清楚是否发生了类似的事情
Java5有显式的锁对象,允许更细粒度的控制,包括简单同步块上的超时,但我不知道它们是否对J2ME有用。Java 5并发LIB有一个后端口,如果这个问题足够大,值得使用它们,那么可以使用J2ME
什么时候发生死锁?
在锁顺序不一致的情况下过度使用同步会导致死锁
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");
}
}
}
避免死锁的解决方案
维持秩序,减少同步
下面是一个导致死锁的场景
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()都将被两个或多个线程调用,则很有可能出现死锁,因为如果ad 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持有字符串锁,线程A也不会被阻止,因为现在线程B不会期望线程A释放Integer锁以继续
你的意思是说一个对象的所有同步方法都将使用相同的锁,因此不可能出现死锁吗?@Albus,声明一个方法
synchronized
相当于将方法内容包含在synchronized(this)
块中。也就是说,所有synchronized
实例方法都锁定在同一个类实例上,因此不可能出现死锁。当然,除非对象外部的其他人锁定同一对象。“当然,除非对象外部的其他人锁定同一对象。”如果锁定对象是