我对Java的分配有一些疑问。
我上课了:
public class Test {
private String s;
public synchronized void setS(String str){
s = s + " - " + str;
}
public String getS(){
return s;
}
}
我在我的setter中使用“synchronized”,并在我的getter中避免它,因为在我的应用程序中,有大量的数据获取,并且设置很少。必须同步设置以避免不一致。我的问题是:获取和设置变量原子?我的意思是,在多线程环境中,Thread1即将设置变量s,而Thread2即将获得“s”。有没有办法让getter方法能得到与s的旧值或s的新值不同的东西(假设我们只有两个线程)? 在我的应用程序中获取新值并不是一个问题,并且获取旧值不是问题。但我可以得到其他东西吗?
考虑到这一点:
public class Test {
private Map<Integer, String> map = Collections.synchronizedMap(new HashMap<Integer, String>());
public synchronized void setMapElement(Integer key, String value){
map.put(key, value);
}
public String getValue(Integer key){
return map.get(key);
}
}
投入和获得原子? HashMap如何处理将元素放入其中?它首先删除旧值并放入现在的值吗?我可以获得旧值或新值以外的其他值吗?
提前致谢!
答案 0 :(得分:6)
在第一种情况下,String
恰好对于不安全的发布是安全的(在“新的”Java内存模型(JMM)中),所以这没关系。
不是volatile
理论上存在一些没有最新值的问题,但是最新的含义并不清楚。您可以使用compare-sand-swap(CAS)循环替换锁定,但这可能不会给您带来很大的性能提升,无论锁定是否可能存在争用。
在HashMap
的情况下,如果有另一个线程写入它,即使是单个写入器线程,也不能安全地读取未同步的映射。实际上,已经发现这会导致运行流行软件的生产系统出现无限循环。问题中的代码实际上对地图使用了两个锁,它位于顶部(尽管如果使用迭代器,您需要显式保持相同的锁)。不是final
会阻止包含类对于不安全的发布是安全的。如果map
为volatile
并且您为每个put
创建了一个新地图,则可以在没有同步的情况下使其安全。
答案 1 :(得分:6)
不要将HashMap包装成某些内容以使其同步,而是考虑使用java.util.concurrency.ConcurrentHashMap
代替。
这是HashMap的更新版本,可以保证“检索反映了最近完成的更新操作的结果。”
答案 2 :(得分:3)
在指出新的(1.5+)JVM时,早期的答案是正确的,String版本是安全的,关于数据损坏。你似乎意识到了下行非同步访问;那些变化不一定是通过吸气剂可见的。
然而:更有用的问题是:有理由在这里进行同步吗?如果这只是为了有兴趣了解这一点,那一切都很好。 但对于实际代码,读写的一般规则是,如果两者都存在,则两者都应该同步。因此,尽管在这种情况下您可以省略同步(可能意味着线程没有看到其他线程所做的更改),但这样做似乎没什么好处。
答案 3 :(得分:2)
也许Read Write Lock可以解决您的问题?
看看它的文档:
读取锁定允许访问共享数据的并发性高于互斥锁定所允许的并发性。它利用了这样一个事实:虽然一次只有一个线程(一个编写器线程)可以修改共享数据,但在许多情况下,任何数量的线程都可以同时读取数据(因此读取器线程)。理论上,使用读写锁所允许的并发性的增加将导致相互使用互斥锁的性能提高。 ....
答案 4 :(得分:1)
在多线程环境中,您需要同步getter,以确保客户端看到最新的s
值。