gpt4 book ai didi

java - 如何在 Java 中创建线程安全的一次写入多次读取值?

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:23:57 25 4
gpt4 key购买 nike

这是我在处理更复杂的系统时经常遇到的问题,而且我一直没有找到解决的好方法。它通常涉及共享对象主题的变体,其构造和初始化必然是两个不同的步骤。这一般是因为架构要求,类似于小程序,所以建议我合并构建和初始化的答案没有用。系统必须最晚以 Java 4 为目标,因此建议仅在更高版本的 JVM 中提供支持的答案也没有用。

举例来说,假设我有一个类,其结构适合这样的应用程序框架:

public class MyClass
{

private /*ideally-final*/ SomeObject someObject;

MyClass() {
someObject=null;
}

public void startup() {
someObject=new SomeObject(...arguments from environment which are not available until startup is called...);
}

public void shutdown() {
someObject=null; // this is not necessary, I am just expressing the intended scope of someObject explicitly
}
}

我无法使 someObject 最终化,因为在调用 startup() 之前无法设置它。但我真的很希望它能反射(reflect)其一次写入的语义,并能够从多个线程直接访问它,最好避免同步。

表达和强制执行一定程度的最终性的想法,我猜想我可以创建一个通用容器,就像这样(更新 - 更正了此类的线程语义):

public class WormRef<T>
{
private volatile T reference; // wrapped reference

public WormRef() {
reference=null;
}

public WormRef<T> init(T val) {
if(reference!=null) { throw new IllegalStateException("The WormRef container is already initialized"); }
reference=val;
return this;
}

public T get() {
if(reference==null) { throw new IllegalStateException("The WormRef container is not initialized"); }
return reference;
}

}

然后在上面的 MyClass 中,执行:

private final WormRef<SomeObject> someObject;

MyClass() {
someObject=new WormRef<SomeObject>();
}

public void startup() {
someObject.init(new SomeObject(...));
}

public void sometimeLater() {
someObject.get().doSomething();
}

这给我带来了一些问题:

  1. 是否有更好的方法或现有的 Java 对象(必须在 Java 4 中可用)?

其次,在线程安全方面:

  1. 这个线程安全的提供没有其他线程访问 someObject.get() 直到它的 set() 已被调用。其他线程只会在 startup() 和 shutdown() 之间调用 MyClass 上的方法 - 框架保证这一点。
  2. 给定完全不同步的 WormReference 容器,在任一 JMM 下是否有可能看到 object 的值既不是 null 也不是对 SomeObject 的引用?换句话说,JMM 是否始终保证没有线程可以观察到对象的内存是分配对象时恰好在堆上的任何值。我相信答案是"is",因为分配显式地将分配的内存清零 - 但 CPU 缓存是否会导致在给定内存位置观察到其他内容?
  3. 是否足以使 WormRef.reference 易变以确保正确的多线程语义?

请注意,这个问题的主要目的是如何表达和执行someObject 的最终性,而不能真正将其标记为final; secondary 是线程安全所必需的。也就是说,不要太在意线程安全方面的问题。

最佳答案

我将从声明您的 someObject volatile 开始。

private volatile SomeObject someObject;

Volatile 关键字创建了一个内存屏障,这意味着在引用 someObject 时,单独的线程将始终看到更新的内存。

在您当前的实现中,一些线程可能仍然将 someObject 视为 null,即使在调用 startup 之后也是如此。

实际上 volatile 技术被 java.util.concurrent 包中声明的集合大量使用。

正如其他一些发帖人在这里建议的那样,如果一切都失败了,则回退到完全同步。

关于java - 如何在 Java 中创建线程安全的一次写入多次读取值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2428725/

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