最终的 hashmap 是线程安全的
final hashmap are thread safe
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)
行得通吗?
线程安全的是对 map
变量的访问:读取该变量的所有线程都将看到相同的对象引用。
然而,HashMap (get/put) 上的操作不是线程安全的,这与 map
是否为 final 无关。
所以你的代码不是线程安全的,除非你在 putValue
方法周围添加一些并发控制 - 例如通过使它成为 synchronized
.
将引用变量设置为 final 确保引用变量不能更改已分配给它的对象引用。
但是对象的值可能会改变。同样的事情发生在这里你的对象值可能会改变。现在需要同步更改值的代码。
您可以使用 ConcurrentHashMap
摆脱所有同步问题。你可以阅读它 here.
使用ConcurrentHashMap
确保对象上的每个写操作一次都应由一个线程处理。它还优化了 HashMap
的阅读。它将 HashMap
分成块,不同的线程可以从不同的块读取。
你的测试有问题。如果两个value
存储了相同的key
,HashMap.put(K key, V value)
will overwrite the former value
with the later。因此,即使没有并发,您的 "test" 也会 return 大小为 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());
, final
does not make your code thread-safe and ,volatile
在这种情况下不剪切。
如果你需要一个线程安全的Hashmap,使用ConcurrentHashMap
。
如果您决心编写自己的Map
interface, I recommend Oracle's Lesson on Concurrency to start with, followed by Brian Goetz's "Java Concurrency in Practice" and maybe a little bit of Javier Fernández González' "Mastering Concurrency Programming with Java 8"线程安全实现。
您问题的直接直接回答是:不,final 关键字不会使字段 线程安全。
该关键字仅告诉编译器它必须确保恰好有 一个 值分配给该字段(不是零或多个分配)。
你知道,获取多线程代码 正确被认为困难[=28=是有原因的].
正确的多线程的本质是:当一些状态可以被一个线程更新,但被其他线程使用(或更新)..以确保你只会得到你想看到的那些状态变化。
长话短说:你有很多东西要学;一个好的起点是 here.
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)
行得通吗?
线程安全的是对 map
变量的访问:读取该变量的所有线程都将看到相同的对象引用。
然而,HashMap (get/put) 上的操作不是线程安全的,这与 map
是否为 final 无关。
所以你的代码不是线程安全的,除非你在 putValue
方法周围添加一些并发控制 - 例如通过使它成为 synchronized
.
将引用变量设置为 final 确保引用变量不能更改已分配给它的对象引用。
但是对象的值可能会改变。同样的事情发生在这里你的对象值可能会改变。现在需要同步更改值的代码。
您可以使用 ConcurrentHashMap
摆脱所有同步问题。你可以阅读它 here.
使用ConcurrentHashMap
确保对象上的每个写操作一次都应由一个线程处理。它还优化了 HashMap
的阅读。它将 HashMap
分成块,不同的线程可以从不同的块读取。
你的测试有问题。如果两个value
存储了相同的key
,HashMap.put(K key, V value)
will overwrite the former value
with the later。因此,即使没有并发,您的 "test" 也会 return 大小为 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());
final
does not make your code thread-safe and volatile
在这种情况下不剪切。
如果你需要一个线程安全的Hashmap,使用ConcurrentHashMap
。
如果您决心编写自己的Map
interface, I recommend Oracle's Lesson on Concurrency to start with, followed by Brian Goetz's "Java Concurrency in Practice" and maybe a little bit of Javier Fernández González' "Mastering Concurrency Programming with Java 8"线程安全实现。
您问题的直接直接回答是:不,final 关键字不会使字段 线程安全。
该关键字仅告诉编译器它必须确保恰好有 一个 值分配给该字段(不是零或多个分配)。
你知道,获取多线程代码 正确被认为困难[=28=是有原因的].
正确的多线程的本质是:当一些状态可以被一个线程更新,但被其他线程使用(或更新)..以确保你只会得到你想看到的那些状态变化。
长话短说:你有很多东西要学;一个好的起点是 here.