首页 > 解决方案 > 静态成员的多线程行为

问题描述

在静态成员的情况下多线程如何表现?就像在单例类的情况下,如果我尝试在静态块和静态方法中创建实例,我返回实例并且两个线程尝试同时执行 getInstance() ..这将如何在内部表现为静态是只加载一次

public class SingleTonUsingStaticInitialization {

    private static SingleTonUsingStaticInitialization INSTANCE = null;

    static {
        try {
            INSTANCE = new SingleTonUsingStaticInitialization();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private SingleTonUsingStaticInitialization() {
    }

    public static SingleTonUsingStaticInitialization getInstance() {
        return INSTANCE;
    }
}

标签: javamultithreadingstaticjava-memory-model

解决方案


这个具体的例子?

线程方面很好。风格明智,这是可悲的。不要写这样的 catch 块。这也意味着如果确实发生了异常(它不能在这里 - 你的构造函数是空的),你的代码会将一半信息转储到系统错误,然后继续,使用 Singleton 实例的空引用实例 - 导致其他吐出 NullPointerExceptions 的代码(因为代码一直在运行,因为您捕获了异常而不是让它发生)。如果您以这种方式处理所有异常,则单个错误将导致日志中出现数百个错误,除了第一个错误之外,所有错误都无关紧要。

一旦你处理了这个异常处理问题,你就可以创建变量final,并且不再将 null 分配给它。当你在它的时候,让整个班级final。它实际上已经是(因为您只有一个私有构造函数):

public final class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Single() {}

    public static Singleton getInstance() {
        return INSTANCE;
}

这在 2 个线程同时调用 getInstance 时起作用的原因是类加载器机制本身:类加载器保证任何给定的类不会被同一个加载器多次加载,即使 2 个线程同时需要这个(类加载器将同步/ lock 以避免这种情况),并且初始化过程(静态块 - 如上例所示,这是不必要的复杂化)同样受到保护,不可能发生两次。

这是您获得的唯一免费赠品:对于静态方法,作为一般规则,所有线程都可以根据需要同时运行相同的方法。他们在这里-只是初始化(包括 ... = new Singleton();部分)被门控只发生一次。

注意:如果您必须做更复杂的事情,请制作辅助方法:

public final class Singleton {
    private static Singleton INSTANCE = create();

    private Singleton(Data d) {
        // do stuff with 'd'
    }

    private static Singleton create() {
        Data d;
        try {
            d = readStuffFromDataIncludedInMyJar();
        } catch (IOException e) {
            throw new Error("states.txt is corrupted", e);
        }
        return new Singleton(d);
    }
}

这个:

  1. 保持代码简单 - 静态初始化器是一件事,但相当奇特的 java。
  2. 使您的代码更易于测试。
  3. 这是一个内部文件;如果它丢失/损坏,那与您的一个类文件出去散步一样可能/同样有问题。此处保证有错误。这不可能发生,除非你写了一个错误或搞砸了一个构建,并且硬崩溃并有一个明确的异常告诉你究竟出了什么问题正是你想要在这种情况下发生的事情,而不是让代码在一半的状态下盲目地继续由于磁盘驱动器崩溃或其他原因,您的应用程序被 gobbledygook 覆盖。最好只是结束一切都无聊,这样说,然后停止运行。

推荐阅读