【问题标题】:java: initialization of static fields of the same class during class initializationjava:类初始化时同类的静态字段的初始化
【发布时间】:2017-11-05 13:03:02
【问题描述】:

似乎在启动应用程序期间发生了死锁。 我有 jstack 文件,在其中我发现一个线程调用 TariffModelManager. 方法,还有其他多个线程调用 TariffModelManager.getInstance() 方法。 TariffModelManager 代码如下:

public class TariffModelManager{
    ...
    private static final TariffModelManager tariffModelManager = TariffModelManager.getInstance();
    ...
    private static volatile TariffModelManager _instance;

    private TariffModelManager(){}

    public static TariffModelManager getInstance() {
        if(_instance == null) {
            synchronized(TariffModelManager.class) {
                if(_instance == null) _instance = new TariffModelManager();
            }
        }
        return _instance;
    }
    ... 
}

我认为,通过调用 getInstance() 方法来初始化静态字段显然是错误的,在该方法中检查了另一个字段 (_instance),代码中描述如下,但到底发生了什么?

  1. 类初始化时,静态字段从上到下初始化;
  2. 我们正在尝试初始化“tariffModelManager”字段...
  3. 调用方法getInstance(),检查_instance字段(但它没有初始化!);
  4. 那么我们必须执行这个类的构造函数;
  5. 如果 _instance 字段已经在构造函数中初始化,是否可以在静态字段初始化序列中设置为 null?

我的问题不是如何解决这个问题,而是描述这段代码执行时到底发生了什么!

更新: Here我找到了详细的类初始化过程,但仍然不清楚“tariffModelManager”和“_instance”字段的初始化顺序。

谢谢!

【问题讨论】:

  • 似乎对我有用。当我添加一个打印TariffModelManager.getInstance() 的主要方法时,它就是这样做的。
  • 是的,我也测试过,但不是在并发环境中。正如我上面所说,在并发环境中使用此代码的应用程序存在问题,我认为这就是原因。
  • 1.在类初始化之前,没有人可以调用getInstance() 方法,并且存在针对多个并发初始化的同步障碍。 2. 正如你自己所说,静态字段是从头到尾初始化的。

标签: java


【解决方案1】:

您可以通过在两个静态字段上放置字段断点来验证发生了什么。

我对你的示例做了一些修改:

class TariffModelManager {
    private static final TariffModelManager tariffModelManager = TariffModelManager.getInstance();
    private static volatile TariffModelManager _instance = new TariffModelManager();

    private TariffModelManager() {
        System.out.println("From Constructor: " + this);
    }

    public static TariffModelManager getInstance() {
        if (_instance == null) {
            synchronized (TariffModelManager.class) {
                if (_instance == null) {
                    _instance = new TariffModelManager();
                    System.out.println("Creating new instance: " + _instance);
                }
            }
        }
        return _instance;
    }

    public static void main(String[] args) {
        TariffModelManager.getInstance();

        System.out.println("tariffModelManager: " + TariffModelManager.tariffModelManager);
        System.out.println("_instance: " + TariffModelManager._instance);
    }
}

这是输出:

From Constructor: TariffModelManager@ea4a92b
Creating new instance: TariffModelManager@ea4a92b
From Constructor: TariffModelManager@3c5a99da
tariffModelManager: TariffModelManager@ea4a92b
_instance: TariffModelManager@3c5a99da

首先初始化静态字段tariffModelManager。当调用 getInstance 方法时,_instance 为 null - 即默认值,尚未初始化。然后它在该方法中被初始化并且值也被分配给tariffModelManager。然后再按照静态初始化顺序重新初始化:Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.

这很有趣,因为

class TariffModelManager {
    private static final TariffModelManager tariffModelManager = _instance;
    private static volatile TariffModelManager _instance = new TariffModelManager();

    private TariffModelManager() {
        System.out.println("From Constructor: " + this);
    }

    public static TariffModelManager getInstance() {
       return _instance;
    }
}

是无效的 java,但静态方法为您提供了一种“解决”它的方法(关税 ModelManager 将为空,不是真正的预期,但仍然可以编译):

class TariffModelManager {
    private static final TariffModelManager tariffModelManager = getInstance();
    private static volatile TariffModelManager _instance = new TariffModelManager();

    private TariffModelManager() {
        System.out.println("From Constructor: " + this);
    }

    public static TariffModelManager getInstance() {
       return _instance;
    }
}

【讨论】:

  • 感谢您的回答!我也测试了这个。这很有趣,但是当我明确地将 _instance 设置为 null 时,会创建两个不同的实例。但是如果我没有明确分配它,即只留下:private static volatile TariffModelManager _instance; 那么“tariffModelManager”和“_instance”字段是一样的!
猜你喜欢
  • 2020-04-14
  • 1970-01-01
  • 2010-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 2020-01-31
  • 2016-12-30
相关资源
最近更新 更多