gpt4 book ai didi

java - 共享对象的线程安全更新没有性能损失?

转载 作者:行者123 更新时间:2023-11-30 08:21:00 27 4
gpt4 key购买 nike

我正在竭尽全力为我的类创建一个线程安全的实现,以便在代码中的 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() 已完成一半)。因此,我可以在 parseConfiguration()buildSentence()Configuration 对象上放置一个同步块(synchronized block),但是我有一个严重的问题性能损失:我不能同时调用多个 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 的引用就可以了中途改变方法。

关于java - 共享对象的线程安全更新没有性能损失?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25562784/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com