public class MapDem {
final HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
public HashMap<Integer,Integer> getMap(){
return map;
}
public void putValue(int key,int value){
map.put(key,value);
}
public static void main(String args[]){
MapDem demo = new MapDem();
new Thread(new Runnable(){
@Override
public void run() {
demo.putValue(1, 10);
}
}).start();
new Thread(new Runnable(){
@Override
public void run() {
demo.putValue(1, 10);
}
}).start();
System.out.println(demo.getMap().size());
}
}
final
字段本身就是线程安全的吗?在上面的代码中,map
变量标记为final
,这是否意味着它是线程安全的?
如果变量不线程安全,我希望main
- 方法的输出应为“2”,但我得到“1”或“0”
修改
如果我使用volatile
关键字声明变量,即
volatile HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
map
变量看似不是线程安全的,为什么会这样?但是下面的方法似乎有效,为什么会这样?
public synchronized void putValue(int key,int value){
if(map.isEmpty()){
System.out.println("hello");
map.put(key,value);
}
使用Collections.unmodifiableMap(map)
会有效吗?
答案 0 :(得分:1)
你的测试有问题。如果两个value
存储有相同的key
,HashMap.put(K key, V value)
will overwrite the former value
with the later。因此,即使没有并发性,您的&#34;测试&#34;将返回1
的大小。
代码:
import java.util.HashMap;
public class MapDem {
final HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
public HashMap<Integer, Integer> getMap() {
return map;
}
public void putValue(int key, int value) {
map.put(key, value);
}
public static void main(String args[]) {
MapDem demo = new MapDem();
demo.putValue(1, 10);
demo.putValue(1, 10);
System.out.println(demo.getMap().size());
}
}
输出(Ideone demo):
1
有时可以看到0
大小的事实是由于缺少阻塞结构。在通过调用join()
on your Thread
-objects查询您的地图大小之前,您应该等待两个Thread
的完成。
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
demo.putValue(1, 10);
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
demo.putValue(1, 10);
}
});
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(demo.getMap().size());
As mentioned by @SachinSarawgi,final
不会使您的代码符合线程安全且as further explained by @assylias,volatile
在这种情况下不会删除它。
如果您需要线程安全的Hashmap,请使用ConcurrentHashMap
。
如果你决定编写Map
interface自己的线程安全实现,我建议Oracle's Lesson on Concurrency开始,然后是Brian Goetz's "Java Concurrency in Practice",可能还有一点Javier Fernández González' "Mastering Concurrency Programming with Java 8" }}
答案 1 :(得分:1)
您问题的直接答案是:不,最终关键字不会使字段线程安全。
该关键字仅告知编译器必须确保为该字段分配了一个值(不是零或多个赋值)。
您知道,获取多线程代码 正确被认为是难以的原因。
正确的多线程的本质是:当某个状态可以被一个线程更新,但被其他线程使用(或更新)时,以确保只获得那些状态您希望看到的更改。
长话短说:你有很多学习要做;一个好的起点是here。
答案 2 :(得分:0)
什么是线程安全的是对Camera.main.aspect = 1F; //for example, a square area
变量的访问:读取该变量的所有线程都将看到相同的对象引用。
然而,HashMap(get / put)上的操作不是线程安全的,这与map
是最终的事实无关。
因此,除非您在map
方法周围添加一些并发控制,否则您的代码不是线程安全的 - 例如将其设为putValue
。
答案 3 :(得分:0)
使参考变量最终确保引用变量不能更改它已分配给它的对象引用。
但是对象的价值可能会发生变化。同样的事情发生在这里你的对象值可能会改变。现在需要同步更改值的代码。
您可以使用ConcurrentHashMap
摆脱所有同步问题。你可以阅读它here。
使用ConcurrentHashMap
确保对象的每个写操作都应该由一个线程一次处理。它也优化了HashMap
的读数。它将HashMap
分成块,不同的线程可以从不同的块中读取。