java 8 Map.merge是线程安全的吗? import java.util.HashMap; 导入java.util.Map; 公共类测试 { 静态映射映射=新的HashMap(); 公共静态无效增量() { 合并(“计数器”,1,整数::和); } 公共静态无效声明() { 合并(“计数器”,-1,整数::和); } 公共静态void main(字符串[]args)引发异常 { 地图放置(“计数器”,0); 对于(int i=0;i
运行main方法时,结果是java 8 Map.merge是线程安全的吗? import java.util.HashMap; 导入java.util.Map; 公共类测试 { 静态映射映射=新的HashMap(); 公共静态无效增量() { 合并(“计数器”,1,整数::和); } 公共静态无效声明() { 合并(“计数器”,-1,整数::和); } 公共静态void main(字符串[]args)引发异常 { 地图放置(“计数器”,0); 对于(int i=0;i,java,java-8,Java,Java 8,运行main方法时,结果是{counter=-2}。 为什么不是0?在Map界面上的merge的Javadoc说: 默认实现不保证此方法的同步性或原子性属性。任何提供原子性保证的实现都必须重写此方法并记录其并发属性 虽然HashMap覆盖默认实现,但它没有关于该实现的并发属性的文档,但它有以下一般性语句: 请注意,此实现不同步。如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了该映射,则必须在外部对其进行同步 因此它不是线程安全的 另外,不清楚为什么在启动相应线程之前调用t1.joi
{counter=-2}
。
为什么不是0?在
Map
界面上的merge
的Javadoc说:
默认实现不保证此方法的同步性或原子性属性。任何提供原子性保证的实现都必须重写此方法并记录其并发属性
虽然HashMap
覆盖默认实现,但它没有关于该实现的并发属性的文档,但它有以下一般性语句:
请注意,此实现不同步。如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了该映射,则必须在外部对其进行同步
因此它不是线程安全的
另外,不清楚为什么在启动相应线程之前调用t1.join()
和t2.join()
如果你把电话倒过来
import java.util.HashMap;
import java.util.Map;
public class MainTest
{
static Map<String, Integer> map = new HashMap();
public static void incr()
{
map.merge("counter", 1, Integer::sum);
}
public static void decr()
{
map.merge("counter", -1, Integer::sum);
}
public static void main(String[] args) throws Exception
{
map.put("counter", 0);
for (int i = 0; i < 10000; i++)
{
Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
incr();
}
});
t1.join();
t1.start();
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
decr();
}
});
t2.join();
t2.start();
}
System.out.println(map);
}
}
到
及
到
您将获得0的输出。当然,如果您这样做,将不会有任何并发修改,因为每个线程将在前一个线程死亡后启动
另一种方法是同步map.merge
外部调用:
t2.start();
t2.join();
对于修改数据结构(如
HashMap
)的特定单一方法来说,要求线程安全性有点奇怪,因为它通常被证明是线程安全的
当您想要同时修改映射时,必须寻找支持并发更新的实现,这通常通过实现ConcurrentMap
来显示。对于这些映射,即使是default
实现也足够了,因为它是在其他接口方法之上实现的,这些接口方法保证是线程安全的。但是,如果您希望保证原子性,则需要一个实现,用适当的实现覆盖该方法,例如:
合并
…整个方法调用是以原子方式执行的。在计算过程中,其他线程在此映射上尝试的某些更新操作可能会被阻止,因此计算应该简短,并且不得尝试更新此映射的任何其他映射
要修复并简化示例,请执行以下操作:
public static void incr()
{
synchronized(map) {map.merge("counter", 1, Integer::sum);}
}
public static void decr()
{
synchronized(map) {map.merge("counter", -1, Integer::sum);}
}
这允许并发度高于2,但由于工作线程可能能够以循环调度新作业的速度执行此简单任务,因此产生的并发度可能仍然接近2。否。为什么会这样
HashMap
不是线程安全的,因此,HashMap
上的任何方法都不是线程安全的。
t2.join();
t2.start();
t2.start();
t2.join();
public static void incr()
{
synchronized(map) {map.merge("counter", 1, Integer::sum);}
}
public static void decr()
{
synchronized(map) {map.merge("counter", -1, Integer::sum);}
}
Map<String, Integer> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10000; i++) {
Thread t1 = new Thread(() -> map.merge("counter", -1, Integer::sum));
Thread t2 = new Thread(() -> map.merge("counter", 1, Integer::sum));
t1.start();
t2.start();
t1.join();
t2.join();
}
System.out.println(map);
ExecutorService threadPool
= Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Map<String, Integer> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10000; i++) {
threadPool.execute(() -> map.merge("counter", -1, Integer::sum));
threadPool.execute(() -> map.merge("counter", 1, Integer::sum));
}
threadPool.shutdown();
threadPool.awaitTermination(1, TimeUnit.DAYS);
System.out.println(map);