[英]Volatile and thread-safety
在以下代码中,
public class Mainly {
private Map<Integer, String> map = new HashMap<Integer, String>();
private volatile int index = 0;
public void set(Integer key, String value) {
map.put(key, value); // (1)
index ++; // (2)
}
public String get(Integer key) {
int i = index; // (3)
return map.get(key); // (4)
}
}
如果(1)
发生于(4)
之前,是否意味着get
方法将始终读取最新值?
很好的问题,在单线程应用程序中,您将无法识别volatile变量的工作,现在让我们看看volatile是如何工作的。
Java volatile关键字用于将Java变量标记为“正在存储在主存储器中”。 更精确地讲,这意味着每次读取易失性变量都将从计算机的主内存中读取,而不是从CPU缓存中读取,并且对volatile变量的每次写入都将写入主内存中,而不仅是CPU缓存中。
设想一种情况,其中两个或多个线程可以访问一个共享对象,该共享对象包含一个声明为以下内容的计数器变量:
public class SharedObject {
public int counter = 0;
}
也可以想象,只有线程1会递增计数器变量,但是线程1和线程2都可能会不时读取计数器变量。
如果计数器变量未声明为volatile,则无法保证何时将计数器变量的值从CPU高速缓存写回主存储器。 这意味着,CPU缓存中的计数器变量值可能与主存储器中的不同。
由于线程尚未看到变量的最新值而导致该变量尚未被另一个线程写回到主内存的问题,被称为“可见性”问题。 一个线程的更新对其他线程不可见。
通过声明计数器变量为volatile,所有对计数器变量的写操作将立即写回到主存储器。 同样,计数器变量的所有读取将直接从主存储器读取。 这是计数器变量的易失性声明的外观:
public class SharedObject {
public volatile int counter = 0;
}
因此,将变量声明为volatile可以保证对该变量的其他写入线程的可见性。
有关volatile关键字的详细说明,请参见: http : //tutorials.jenkov.com/java-concurrency/volatile.html
让我知道您是否仍然对挥发物有些困惑。
如果您明白我的意思,请单击对号以接受答案。
编码愉快!! 民间
看来您正在尝试使读/写操作线程安全。
您做错了方法,首先是将Mainly类用作单例吗?
如果是,则应该同步您的set
和get
方法,例如:
public synchronized void set(Integer key, String value) {
map.put(key, value);
index ++;
}
public synchronized String get(Integer key) {
int i = index;
return map.get(key);
}
这样,如果正确封装了字段,则所有线程将同时看到map
和index
的相同值。
这里不需要使用volatile
关键字,因为在Java中前缀/后缀的增加/减少不是原子的。 就您而言,仅确保同步是不够的。
我有一个全局Map,只有一个线程对其进行更新,并且有多个线程读取map中的值。是否可以这种方式读取map中的最新值?
您可能正在寻找ConcurrentHashMap。 从API:
检索反映了自发生以来最新完成的更新操作的结果。
但是仍然不能保证在读取之前启动的更新将完成。 您的返回值可能有点陈旧,但至少可以保证它是最新的有效值,而不是某些处于修改中的正在处理的状态。
回答原始问题,不,在索引变量上使用volatile在这里无济于事。 其他人已经对此给出了很好的解释。 两个主要选项是对读取和写入使用线程限制,或使用同步。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.