从线程内部修改外部对象
Modify outer object from inside a thread
public void fooAndBar() {
HashMap<Foo, Bar> fooBarMap = new HashMap<>();
CompletionService completionService = new ExecutorCompletionService(exec);
for(int i=0; i<10; i++) {
completionService.submit(new Callable() {
@Override
public Void call() throws Exception {
fooBarMap.put(new Foo(i), new Bar(i));
return null;
}
});
}
}
修改Callable
里面的HashMap安全吗?
hashmap 应该是 final
(或者可能是 volatile
),如果是,为什么?
我是否应该使用 HashMap
以外的结构,例如 ConcurrentHashMap
或 SynchronizedMap
,为什么?
我正在努力掌握 java 概念,请多多包涵
Hashmap 不应是最终的,因为您要对其进行多次修改(在 for 循环中)。
如果将其设为最终版本,则可能会出错。
Is it safe to modify the HashMap inside the Callable?
没有。如果您使用的是线程池,我假设您计划并行使用更多这些可调用对象 运行。任何时候从多个线程访问具有可变状态的对象时,都是线程不安全的。如果同时从两个线程写入一个线程不安全的 hashmap,其内部结构将被破坏。如果您从一个线程不安全的 hashmap 中读取,而另一个线程正在同时写入它,您的读取线程将读取垃圾。这是一种众所周知且经过广泛研究的情况,称为 竞争条件 ,对此的描述完全超出了本答案的范围。有关详细信息,请阅读 Race Condition on Wikipedia or on another question answered back in 2008: Whosebug - What is a Race Condition。
Should the hashmap be final (or maybe volatile) and if so, why?
为了您的目的,它不需要是最终的,但最好将任何可以成为最终的东西都设为最终。
不需要 volatile 因为:
如果你要让它变成易变的,你会把它的引用变成易变的,但引用永远不会改变,改变的是它的内容,而易变与那些无关。
线程池确保 call()
将在 fooBarMap = new HashMap<>()
之后执行。 (如果你想知道为什么这样的事情会成为一个问题,google for "memory boundary"。)
Should I use a structure other than HashMap, something like ConcurrentHashMap or SynchronizedMap and why?
当然。因为,正如我之前所写,任何时候从多个线程访问具有可变状态的对象时,都是线程不安全的。 ConcurrentHashMap
、SynchronizedMap
、synchronize
等正是为了处理线程不安全的情况而存在的。
public void fooAndBar() {
HashMap<Foo, Bar> fooBarMap = new HashMap<>();
CompletionService completionService = new ExecutorCompletionService(exec);
for(int i=0; i<10; i++) {
completionService.submit(new Callable() {
@Override
public Void call() throws Exception {
fooBarMap.put(new Foo(i), new Bar(i));
return null;
}
});
}
}
修改
Callable
里面的HashMap安全吗?hashmap 应该是
final
(或者可能是volatile
),如果是,为什么?我是否应该使用
HashMap
以外的结构,例如ConcurrentHashMap
或SynchronizedMap
,为什么?
我正在努力掌握 java 概念,请多多包涵
Hashmap 不应是最终的,因为您要对其进行多次修改(在 for 循环中)。
如果将其设为最终版本,则可能会出错。
Is it safe to modify the HashMap inside the Callable?
没有。如果您使用的是线程池,我假设您计划并行使用更多这些可调用对象 运行。任何时候从多个线程访问具有可变状态的对象时,都是线程不安全的。如果同时从两个线程写入一个线程不安全的 hashmap,其内部结构将被破坏。如果您从一个线程不安全的 hashmap 中读取,而另一个线程正在同时写入它,您的读取线程将读取垃圾。这是一种众所周知且经过广泛研究的情况,称为 竞争条件 ,对此的描述完全超出了本答案的范围。有关详细信息,请阅读 Race Condition on Wikipedia or on another question answered back in 2008: Whosebug - What is a Race Condition。
Should the hashmap be final (or maybe volatile) and if so, why?
为了您的目的,它不需要是最终的,但最好将任何可以成为最终的东西都设为最终。
不需要 volatile 因为:
如果你要让它变成易变的,你会把它的引用变成易变的,但引用永远不会改变,改变的是它的内容,而易变与那些无关。
线程池确保
call()
将在fooBarMap = new HashMap<>()
之后执行。 (如果你想知道为什么这样的事情会成为一个问题,google for "memory boundary"。)
Should I use a structure other than HashMap, something like ConcurrentHashMap or SynchronizedMap and why?
当然。因为,正如我之前所写,任何时候从多个线程访问具有可变状态的对象时,都是线程不安全的。 ConcurrentHashMap
、SynchronizedMap
、synchronize
等正是为了处理线程不安全的情况而存在的。