java - 单例类中的方法是线程安全的吗?

标签 java multithreading thread-safety

我正在为我们的应用程序设计一个自定义日志记录框架。

我正在阅读Patterns For Logging Diagnostic Messages ,正在 http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/NDC.html 中使用

第 3 页上写着:

Because it is a Singleton, it easily preserves the order of the messages.

我认为,例如,给定 Singleton 类 S,如果类 B 试图获取 S 的实例,而类 A 已经获取了实例 S,则 B 无法获取 S 的实例,因为 A 已经获取了该实例S.

这就是根据我的理解保留消息顺序的原因。

  1. 我的理解正确吗?

  2. 类 B 如何知道类 A 已完成类 S 并且不再需要它,以便 B 可以获取 S?

  3. 如果我的理解是正确的,并且 Singleton 类 S 是否有一些方法: test1() 和 test2() ,如下所示。

test1() 和 test2() 是线程安全的吗?

这些方法将在类 S 之外调用,类似于

S.getInstance().test1("消息")

例如,A 类或 B 类。

这意味着当类 A 和类 B 尝试通过调用 test1() 在日志文件中写入一些数据时,这些数据将按照获取 S 实例的顺序写入?

如果不是,为了使单例类 S 中的方法线程安全,我是否还应该在方法 test1() 和 test2() 上使用 synchronized 关键字或 lock 进行这些方法?

public class S {                  // singleton class S

    private static S instance;

    private S(){}

    public static S getInstance(){
        if(instance == null){
            instance = new S();
        }
        return instance;
    }

    public static void test1(String log) {
       // writing some data to a log file
    }

    public static void test2(String log) {
       // writing some data to a log file
    }
}

最佳答案

这绝对不是线程安全的。假设我有两个线程 T1 和 T2,并且 S 具有属性 foo。假设T1和T2正在修改foo的值,然后使用foo的值执行一些其他操作。

然后,我可能可以让 T1 访问 S.getInstance,检查 getInstance 是否未设置,同时,T2 可以访问 S.getInstance 并查看该实例是否未设置。然后,T1 可能会设置实例,但由于 T2 也同时检测到实例未设置,因此也会为 S 设置实例。因此,S.instance 的值实际上将是由T2设定。换句话说,T1 和 T2 之间存在竞争条件,看谁可以先设置 S 的实例。

为了使其同步,您绝对应该同步 getInstance 方法,这样只有一个线程可以同时对其进行操作。另外,您可能应该使 S 的实例成为 volatile 的,以确保访问 S 实例的任何线程始终使用“最新”副本。 (因为可能有一个线程在修改该实例时可能正在对该实例执行其他一些读取操作)。

即像这样的东西:

public class S {                  // singleton class S

    private volatile static S instance;

    private S(){}

    public synchronized static S getInstance(){
        if(instance == null){
            instance = new S();
        }
        return instance;
    }

    public static void test1(String log) {
       // writing some data to a log file
    }

    public static void test2(String log) {
       // writing some data to a log file
    }
}

另外,这里有一个很好的链接,说明为什么应该使用 volatile:

What is the point of making the singleton instance volatile while using double lock?

关于java - 单例类中的方法是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48714194/

相关文章:

java - 分割后读取内容

java - 如何正确启动Jetty

java - 如何在 Jackson 的自定义解串器中调用默认解串器?

java - RecyclerView 没有更新 OnPostExecute() 上的项目

java - 在java代码中哪里实现错误消息

java - 具有无限循环阻塞 gui 更新 Netbeans 的独立线程

multithreading - 内存屏障/栅栏如何抑制 CPU 执行的指令重新排序?

multithreading - 在线程环境中不保护共享变量的危险

c++ - 是否有开源线程安全的 C++ 对象池实现?

c++ - 带有 std::condition 变量的 Clang 线程安全