gpt4 book ai didi

java - 静态初始化程序和静态同步方法锁定问题

转载 作者:搜寻专家 更新时间:2023-11-01 02:54:23 25 4
gpt4 key购买 nike

我在我的应用程序中遇到了一些锁定问题,其中包含如下几个类:

public interface AppClient {
void hello();
}

public class Client implements AppClient {
public synchronized static AppClient getInstance() {
return instance;
}

public void hello() {
System.out.println("Hello Client");
}

private final static class InnerClient implements AppClient {
public void hello() {
System.out.println("Hello InnerClient");
}
}
private static AppClient instance;

static {
instance = new InnerClient();
doSomethingThatWillCallClientGetInstanceSeveralTimes();
}
}

public class Application {
new Thread() {
AppClient c = Client.getInstance();
c.hello();
}.start();
new Thread() {
AppClient c = Client.getInstance();
c.hello();
}.start();
// ...
new Thread() {
AppClient c = Client.getInstance();
c.hello();
}.start();
}

在 doSomethingThatWillCallClientGetInstanceSeveralTimes() 方法中,它会做很多初始化工作,涉及很多类,并在初始化过程中多次循环调用 Client.getInstance 静态方法(我知道这不好,但是,这是遗留代码库持续 20 多年)。

这是我的问题:

1)我以为在类Client初始化完成之前,只有第一个触发类Client初始化的线程才能访问Client.getInstance方法,因为JVM会在类初始化完成之前在Client.class对象上进行同步。我阅读了相关主题的 JLS 并得出了这个结论(第 12.4.2 节,详细的初始化过程,http://java.sun.com/docs/books/jls/third_edition/html/execution.html)。

2) 但是,这不是我在真实环境中看到的行为。比如有3个线程调用了Client.getInstance(),thread-1触发了Client.class的初始化,在doSomethingThatWillCallClientGetInstanceSeveralTimes()方法中多次调用了Client.getInstance()。而在doSomethingThatWillCallClientGetInstanceSeveralTimes()方法完成之前,thread-2获取了Client.class对象的锁(这怎么可能?但是确实发生了),然后进入了Client.getInstance方法(因为这个方法是静态同步方法) .由于某种原因,线程 2 无法返回“实例”(我猜它正在等待 Client.class 完成其初始化)。同时,thread-1 无法继续执行,因为它仍然需要在 doSomethingThatWillCallClientGetInstanceSeveralTimes() 中调用 Client.getInstance,并且由于它属于 thread-2,因此无法获取锁。 Threaddump 告诉我线程 2 处于 RUNNABLE 状态,线程 1 处于 BLOCKED 状态,等待线程 2 拥有的锁。

我只能在 Windows 的 64 位 Java 6u23 JVM 中重现此行为,无法在 32 位 Java 6 JVM + Windows 环境中重现。有人能告诉我我在这里想念什么吗?这种代码是不是注定会产生这样的锁,如果是,又是怎么来的呢?我对这部分的 JLS 的理解不正确吗?还是 JVM 问题?任何帮助表示赞赏。谢谢。

最佳答案

对我来说,这看起来像是一个错误。当一个线程正在调用静态 block 时,没有其他线程应该能够访问它。该错误可能是另一个线程可以在初始化完成之前获取该类的锁。 :(

我建议您构建代码,这样您就不需要在启动时进行这种锁定。听起来确实很复杂。例如在您的示例中,客户端不需要扩展客户端,并且可以在其声明的行上初始化实例。我会考虑以下结构。

enum Client implements AppClient {
INSTANCE;

public void hello() {
System.out.println("Hello Client");
}
}

您可以使 Client 可变或使用委托(delegate),这样它就不会暴露它可以更改状态(或实现)的事实

关于java - 静态初始化程序和静态同步方法锁定问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4612492/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com