我正在努力使我的类的线程安全实现可以在Servlet或代码中的多个“客户端”之间共享。

假设我有以下MySingleton类,它是用Configuration对象初始化的单例。但是,可以观察到Configuration对象,因此如果进行了更改,则单例将订阅通知。重要要点:


该配置可以随时更改(无法预测)
解析配置后,其值将保存到MySingleton的成员字段中
单例的public方法使用这些字段来生成返回结果


请参见下面的简化代码:

public class MySingleton
    implements IConfigurationObserver {
    // Member(s)
    private static volatile MySingleton instance;
    private final Configuration configuration;
    private String firstParam;
    private String secondParam;

    // Constructor(s)
    private MySingleton(Configuration configuration) {
        this.configuration = configuration;
        parseConfiguration();
        configuration.addObserver(this);
    }

    public static MySingleton getInstance(Configuration configuration) {
        // Perform synchronized creation if applicable (double locking technique)
        MySingleton syncInstance = instance;
        if (syncInstance == null) {
            synchronized(MySingleton.class) {
                syncInstance = instance; // Verify once again after synchronizing
                if(syncInstance == null) {
                    syncInstance = new MySingleton(configuration);
                    instance = syncInstance;
                }
            }
        }

        return syncInstance;
    }

    // Private Method(s)
    private void parseConfiguration() {
        // Import values from the configuration
        this.firstParam = configuration.get(0);
        this.secondParam = configuration.get(1);
    }

    // Public Method(s)
    public String buildSentence() {
        // Build a new string based on values pulled from the configuration
        StringBuilder strBuilder = new StringBuilder();
        strBuilder.append(firstParam);
        strBuilder.append(" - ");
        strBuilder.append(secondParam);

        return strBuilder.toString();
    }

    // Observer Method(s)
    @Override
    public void onConfigurationUpdated() {
        // The configuration has changed. Parse it again.
        parseConfiguration();
    }
}


该类自己可以正常工作(在单线程环境中),但是在多线程场景中,我想消除一些威胁:


如果在很短的时间内对Configuration进行了两次更新,则可能在第二个调用开始之前没有完成对parseConfiguration()的第一次调用。这很容易解决,我可以使parseConfiguration()同步(对吗?)。但...
假设当配置通知我们单身人士时,我们正处于对buildSentence()的调用中。我不希望buildSentence()为firstParam和secondParam使用旧值的混合(即,如果parseConfiguration()已完成一半)。这样,我可以在ConfigurationparseConfiguration()buildSentence()对象上放置一个同步块,但是这样会严重影响性能:对buildSentence()的并发调用不能超过一个。实际上,对我来说理想的情况是:


如果buildSentence()正在运行并且发生Configuration更新,则parseConfiguration()必须等到buildSentence()结束后再运行
如果parseConfiguration()正在运行,则对buildSentence()的调用必须等到parseConfiguration()结束后才能开始
但是,一旦parseConfiguration()完成,我想允许多个线程同时运行buildSentence()。只有在即将进行更新或正在进行更新时,才应进行锁定。



我如何重构MySingleton以允许上面列出的理想“规则”?可能吗?



我一直在想一个涉及信号量的解决方案。即:执行buildSentence()时,请检查信号灯是否可用。如果是,请继续(无阻塞)。如果否,请等待。并且parseConfiguration将在信号执行期间锁定信号量。但是,如果有人有直接的建议方法,我不想过度设计这个问题。让我知道!

最佳答案

我想我会喜欢这样的东西:

public class MySingleton
    implements IConfigurationObserver {
    // Member(s)
    private static volatile MySingleton instance;
    private final Configuration configuration;
    private volatile ParsedConfiguration currentConfig;

    // Constructor(s)
    private MySingleton(Configuration configuration) {
        this.configuration = configuration;
        parseConfiguration();
        configuration.addObserver(this);
    }

    public static MySingleton getInstance(Configuration configuration) {
        // Perform synchronized creation if applicable (double locking technique)
        MySingleton syncInstance = instance;
        if (syncInstance == null) {
            synchronized(MySingleton.class) {
                syncInstance = instance; // Verify once again after synchronizing
                if(syncInstance == null) {
                    syncInstance = new MySingleton(configuration);
                    instance = syncInstance;
                }
            }
        }

        return syncInstance;
    }

    // Private Method(s)
    private void parseConfiguration() {
        // Import values from the configuration
        currentConfig = configuration.getNewParsedConfiguration();
    }

    // Public Method(s)
    public String buildSentence() {
        // Build a new string based on values pulled from the current configuration
        ParsedConfiguration configInUse = currentConfig;
        StringBuilder strBuilder = new StringBuilder();
        strBuilder.append(configInUse.getFirstParam());
        strBuilder.append(" - ");
        strBuilder.append(configInUse.getSecondParam());

        return strBuilder.toString();
    }

    // Observer Method(s)
    @Override
    public void onConfigurationUpdated() {
        // The configuration has changed. Parse it again.
        parseConfiguration();
    }
}


请注意将currentConfig移到buildSentence开头的局部变量的安全措施,这样对使用的ParsedConfig的引用就不能更改中间方法。

08-17 18:39