[英]Do I need to use volatile, if 2 different write and read thread will never alive at the same time
通过参考http://www.javamex.com/tutorials/synchronization_volatile.shtml , 由于附加规则3 ,我不确定在下列情况下是否需要使用volatile
关键字。
线程A写入的新值是否会在“死”之后始终提交给主内存? 如果是,是否意味着如果满足上述3个条件,我不需要volatile
关键字?
我怀疑在这种情况下需要volatile
。 根据需要, ArrayList可能会损坏。 由于一个线程可以执行insert和update size
成员变量。 之后,另一个线程(不兼容)可能会读取ArrayList
的size
。 如果查看ArrayList
源代码,则不ArrayList
size
声明为volatile。
在ArrayList
JavaDoc中,只提到ArrayList
不能安全地用于多个线程同时访问ArrayList实例 ,但不能让多个线程在不同的时间访问ArrayList实例 。
让我使用以下代码来解决此问题
public static void main(String[] args) throws InterruptedException {
// Create and start the thread
final ArrayList<String> list = new ArrayList<String>();
Thread writeThread = new Thread(new Runnable() {
public void run() {
list.add("hello");
}
});
writeThread.join();
Thread readThread = new Thread(new Runnable() {
public void run() {
// Does it guarantee that list.size will always return 1, as this list
// is manipulated by different thread?
// Take note that, within implementation of ArrayList, member
// variable size is not marked as volatile.
assert(1 == list.size());
}
});
readThread.join();
}
是的,您仍然需要使用volatile(或其他形式的同步)。
原因是两个线程可以在不同的处理器上运行,即使一个线程在另一个线程开始之前已经完成了很长时间,也不能保证第二个线程在进行读取时会获得最新的值。 如果该字段未标记为volatile并且未使用其他同步,则第二个线程可以获取在其运行的处理器上本地缓存的值。 理论上缓存的值可能在很长一段时间内都是过时的,包括在第一个线程完成之后。
如果使用volatile,则将始终将值写入主内存并从主内存中读取,从而绕过处理器的缓存值。
不,你可能不需要它。 尽管Mark Byers的回答开始相当准确,但它是有限的。 synchronized和volatile不是在线程之间正确传递数据的唯一方法。 还有其他一些较少谈论“同步点”。 具体而言,线程开始和线程结束是同步点。 但是,启动线程B的线程必须已经识别出线程A已完成(例如,通过连接线程或检查线程的状态)。 如果是这种情况,则变量不需要是volatile。
可能是的,除非您手动创建内存屏障。 如果A设置变量,并且B决定从某个注册表中取出oit,那么就会出现问题。 因此,您需要一个内存屏障,隐式(锁定,易失性)或显式。
http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.4
线程T1中的最终操作与另一个检测到T1已终止的线程T2中的任何操作同步。 T2可以通过调用T1.isAlive()或T1.join()来完成此操作。
因此,可以在不使用volatile的情况下实现目标。
在许多情况下,当存在明显的时间依赖性时,同事正在由人员进行同步,并且应用程序不需要额外的同步。 不幸的是,这不是规则,程序员必须仔细分析每个案例。
一个例子是Swing工作者线程。 人们会在工作线程中进行一些计算,将结果保存到变量,然后引发事件。 然后,事件线程将从变量中读取计算结果。 应用程序代码不需要显式同步,因为“引发事件”已经进行了同步,因此工作线程的写入在事件线程中是可见的。
一方面,这是一种幸福。 另一方面,许多人不理解这一点,他们省略了同步只是因为他们从未想过这个问题。 他们的节目恰好是正确的......这一次。
如果线程A在线程B开始读取之前肯定死亡,则可以避免使用volatile
例如。
public class MyClass {
volatile int x = 0;
public static void main(String[] args) {
final int i = x;
new Thread() {
int j = i;
public void run() {
j = 10;
final int k = j;
new Thread() {
public void run() {
MyClass.x = k;
}
}.start();
}
}.start();
}
}
但问题是,无论Thread启动哪个线程B,现在都需要线程A写入的值已经改变,并且不使用自己的缓存版本。 最简单的方法是让线程A生成线程B.但是如果线程A在生成线程B时没有别的事情可做,那么这似乎有点无意义(为什么不使用相同的线程)。
另一个替代方案是,如果没有其他线程依赖于此变量,则可能线程A可以使用volatile变量初始化局部变量,执行它需要执行的操作,然后最终将其局部变量的内容写回volatile变量。 然后,当线程B启动时,它从volatile变量初始化其局部变量,然后仅从其局部变量读取。 这应该大大减少保持volatile变量同步所花费的时间。 如果这个解决方案似乎不可接受(因为其他线程写入volatile变量或其他),那么你肯定需要声明变量volatile。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.