我知道这个问题听起来很疯狂,但请考虑以下java代码段:
部分 - 我:
class Consumer implements Runnable{
private boolean shouldTerminate = false
public void run() {
while( !shouldTerminate ){
//consume and perform some operation.
}
}
public void terminate(){
this.shouldTerminate = true;
}
}
所以,第一个问题是,我是否需要在shouldTerminate布尔值上进行同步?如果是这样的话?我不介意将标志设置为true一个或两个周期(循环= 1循环执行)。第二,布尔变量是否可能处于不一致状态?(除了true或false之外的其他任何东西)
问题的第二部分:
class Cache<K,V> {
private Map<K, V> cache = new HashMap<K, V>();
public V getValue(K key) {
if ( !cache.containsKey(key) ) {
synchronized(this.cache){
V value = loadValue(key)
cache.put(key, value);
}
}
return cache.get(key);
}
}
是否应同步访问整个地图?是否有可能两个线程尝试运行此方法,一个“编写器线程”在将值存储到映射的过程中,同时,“读取器线程”调用“包含”方法。这会导致JVM爆炸吗? (我不介意在地图中覆盖值 - 如果两个编写器线程同时尝试加载)
答案 0 :(得分:4)
这两个代码示例都破坏了并发性。
第一个至少需要标记为volatile
的字段,否则另一个线程可能永远不会看到变量被更改(它可能将其值存储在CPU缓存或寄存器中,而不检查内存中的值是否存在已经改变了。)
第二个更加破碎,因为HashMap
的内部不是线程安全的,它不仅仅是单个值而是复杂的数据结构 - 从许多线程中使用它会产生完全不可预测的结果。一般规则是必须同步读取和写入共享状态。您也可以使用ConcurrentHashMap
来获得更好的效果。
答案 1 :(得分:2)
除非您对变量进行同步,或将变量标记为volatile,否则无法保证对象的单独线程视图得到协调。引用Wikipedia artible on the Java Memory Model
主要的警告是,as-if-serial语义不会阻止不同的线程拥有不同的数据视图。
实际上,只要两个线程在某些时某些锁上同步,就会看到变量的更新。
我想知道为什么你不想标记变量volatile
?
答案 2 :(得分:1)
并不是说JVM会“爆炸”。但是这两种情况都是错误的同步,因此结果将是不可预测的。最重要的是,JVM设计为以特定方式运行如果以特定方式同步;如果你没有正确同步,你将失去这种保证。
人们认为他们找到了可以省略某些同步的原因,或者在不知不觉中省略了必要的同步但没有立即出现明显问题的情况并不少见。但是由于同步不充分,您的程序可能出现在一个环境中正常工作,只有在特定因素发生变化时才出现问题(例如,转移到具有更多CPU的计算机) ,或添加特定优化的JVM更新。
答案 3 :(得分:0)
shouldTerminate
:请参阅
Dilum的回答cache.containsKey(key)
同时
另一个线程正在调用
cache.put(key, value)
ConcurrentModificationException
)put
调用导致地图出现问题,可能会发生错误
成长,但通常会工作(比失败更糟)。