Java 保证将成员变量值写入内存而不将其标记为volatile
我有以下课程:Java 保证将成员变量值写入内存而不将其标记为volatile,java,multithreading,Java,Multithreading,我有以下课程: public class Example { private AtomicBoolean isValueSet = new AtomicBoolean(false); private int value = 0; // I DON'T want to make this volatile. private void initializeValue(int value) { // as you can see, value can onl
public class Example {
private AtomicBoolean isValueSet = new AtomicBoolean(false);
private int value = 0; // I DON'T want to make this volatile.
private void initializeValue(int value) {
// as you can see, value can only ever be set once.
if (isValueSet.compareAndSet(false, true)) {
this.value = value;
}
}
public int getValue() {
return value;
}
}
Example
类有一个值,该值只初始化一次,但必须在创建Example
实例后初始化。这意味着我不能在构造函数中设置它(使其成为最终的)Example.value
是一个写一次、读多个成员变量
问题:
我在多线程环境中工作,因此可以在线程1
上初始化该值,并在线程2
上读取该值。这意味着在线程1
上设置该值后,写入操作可能尚未从缓存刷新到内存,这意味着写入操作对线程2
不可见
在您进一步阅读之前,我想说明的是,这个问题的关注点不是可能存在的任何竞争条件,而是确保initializeValue()
调用确保将值写入内存
虽然这是最简单的解决方案,但我在一个多线程环境中工作,性能是关键,因此我不想让成为示例。value
volatile
问题:
现在,我对“先发制人”原则的理解意味着,在synchronized
块中设置的任何值都会自动刷新到内存中,因此我可以通过以下更改来解决我的问题:
private void initializeValue(int value) {
// as you can see, value can only ever be set once.
if (isValueSet.compareAndSet(false, true)) {
synchronized (new Object()) {
this.value = value;
}
}
}
这是正确的吗
编辑:
鉴于我们已确定上述措施无效,以下措施是否有效:
public class UnsafeUtil {
private static final Unsafe unsafe;
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (sun.misc.Unsafe) field.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void setValue(Object instance, String fieldName, Object value) {
try {
Field field = instance.getClass().getDeclaredField(fieldName);
unsafe.putObjectVolatile(instance, unsafe.objectFieldOffset(field), value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class Example {
private AtomicBoolean isValueSet = new AtomicBoolean(false);
private Object value = null;
private void initializeValue(Object value) {
// as you can see, value can only ever be set once.
if (isValueSet.compareAndSet(false, true)) {
UnsafeUtil.setValue(this, "value", value);
}
}
public Object getValue() {
return value;
}
}
不,不正确。值的读取器必须在同一对象上同步以读取值,然后,在应用原则之前发生
发件人:
监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法进入)之前。而且因为“发生在之前”关系是可传递的,所以在解锁之前线程的所有操作都发生在监视的任何线程锁定之后的所有操作之前
不,不正确。值的读取器必须在同一对象上同步以读取值,然后,在应用原则之前发生
发件人:
监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法进入)之前。而且因为“发生在之前”关系是可传递的,所以在解锁之前线程的所有操作都发生在监视的任何线程锁定之后的所有操作之前
您当前的方法不正确,无法保证写入的可见性。我的建议是,一定要使用volatile变量来保存该值,如果volatile读取确实是并发应用程序经验证的瓶颈,请调整设计,使每个线程只读取一次volatile值,并将其缓存在某个线程本地存储中。还要注意的是,即使是一个普通的非易失性变量,在写入变量后启动的线程也能保证看到写入操作。您当前的方法不正确,不能保证写入操作的可见性。我的建议是,一定要使用volatile变量来保存该值,如果volatile读取确实是并发应用程序经验证的瓶颈,请调整设计,使每个线程只读取一次volatile值,并将其缓存在某个线程本地存储中。还请注意,即使是普通的非易失性变量,在写入变量后启动的线程也能确保看到写入操作。否,这是不正确的<在随机对象上进行代码>同步
是毫无意义的。读取和写入必须在同一对象上的同步
块内发生(或在同一变量的volatile
读取/写入上发生)。您的volatile
问题似乎有误。这是适合此作业的工具。您的同步块没有完全同步,它会为每个线程锁定不同的对象。您必须使值
易失性
。在随机对象上同步不会对您有任何帮助。而且很可能比volatile慢。我同意其他人的观点,你需要一个内存屏障来正确读取值。可能会提供一些在这方面有帮助的功能。我一直在研究AtomicReference
并注意到Unsafe
的使用……经过一些研究,这可能正是我想要的。不,这是不正确的<在随机对象上进行代码>同步
是毫无意义的。读取和写入必须在同一对象上的同步
块内发生(或在同一变量的volatile
读取/写入上发生)。您的volatile
问题似乎有误。这是适合此作业的工具。您的同步块没有完全同步,它会为每个线程锁定不同的对象。您必须使值
易失性
。在随机对象上同步不会对您有任何帮助。而且很可能比volatile慢。我同意其他人的观点,你需要一个内存屏障来正确读取值。在这种情况下,可能会提供一些有帮助的功能。我一直在研究AtomicReference
,并注意到Unsafe
的使用……经过一些研究,这可能正是我想要的。我找不到我阅读它的来源,但我的印象是,在任何监视器上同步的任何块内设置的任何值都会在释放montior时写入内存…猜不到:p…感谢您的响应。我找不到读取它的来源,但我在