首页 > 解决方案 > Java单例类和多线程

问题描述

伙计们——我有一个助手类,它的任务是根据参数构建一些消息。该类本身没有任何私有数据(当然实例除外)。

public class RequestBuilder {
    private static RequestBuilder instance = new RequestBuilder();

    private RequestBuilder() {}

    public static RequestBuilder getInstance() {
        return instance;
    }

    public SetRequest buildSetRequest(Path prefix,
                                      Path path,
                                      ConfigEntity configEntity,
                                      Any protoAnyData) {
        .....
        .....
        return setRequest;
    }

    public GetRequest buildGetRequest(Path prefix,
                                      Path Path,
                                      RetrieveRequestEntity retrieveRequestEntity,
                                      Encoding encoding) {
        .....
        .....
        return getRequest;
    }
}

我了解单例类对多线程不友好。在这种情况下,当 2 个线程尝试同时执行 buildSetRequest() 时会发生什么?

谢谢你的时间。

编辑: 根据我的需要,正如@BoristheSpide 在下面的评论中所建议的那样,我将把这个类作为一个实用类进行以下更改: 1. 使其成为最终的。2. 使方法静态化。3. 删除所有单例引用。

public final class RequestBuilder {

    private RequestBuilder() {}

    public static SetRequest buildSetRequest(Path prefix,
                                      Path path,
                                      ConfigEntity configEntity,
                                      Any protoAnyData) {
        .....
        .....
        return setRequest;
    }

    public static GetRequest buildGetRequest(Path prefix,
                                      Path Path,
                                      RetrieveRequestEntity retrieveRequestEntity,
                                      Encoding encoding) {
        .....
        .....
        return getRequest;
    }
}

我将保留原始代码,因为它仍然有效,并为该问题的评论和答案提供了上下文。

标签: javamultithreadingsingleton

解决方案


在这种情况下,不要太多,因为您的构造函数是空的(其他人提到的没有共享状态)。当您有多个必须初始化的私有实例变量时,问题就开始了。在这种情况下,您需要一些保护,例如 doble-check:

private static volatile RequestBuilder instance;

private RequestBuilder() {}

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

原因是线程可以随时挂起。如果当前构造实例的线程被挂起,而另一个线程来了,则可能有一半初始化的实例变量,并且对象可能最终处于损坏状态。

编辑:关于buildSetRequest()

代码在方法内部,如果该方法自己创建自己的实例或与线程安全类一起使用,则不会有任何问题。


推荐阅读