原子操作组合与线程安全

35次阅读

共计 966 个字符,预计需要花费 3 分钟才能阅读完成。

除了操作原子性之外,还有一个比较容易引起线程不安全的原因:安全方法组合。使用多个线程安全的方法组合成一个方法,也有可能导致线程不安全的情况出现。
以 ConcurrentHashMap 类为例,ConcurrentHashMap 是一个高并发高性能的 map 实现类,他的每个方法都是线程安全的。
下面是示例代码:

public class TestTwoAtomicMethods {private final ConcurrentHashMap<Integer,Integer>  map = new  ConcurrentHashMap<Integer,Integer>();
    @Interleave
    public void update()  {Integer result = map.get(1);        
            if(result == null)  {map.put(1, 1);
            }
            else    {map.put(1, result + 1);
            }    
    }
    @Test
    public void testUpdate() throws InterruptedException    {Thread first  = new Thread( () -> {update();   }  );
        Thread second = new Thread(() -> {update();   }  );
        first.start();
        second.start();
        first.join();
        second.join();}    
    @After
    public void checkResult() {assertEquals( 2 , map.get(1).intValue());
    }    
}

下面是控制台打印信息:

至于为什么会这样的,原因是因为在代理第 5 行执行完之后,在下面复制的判断过程中依然存在着多个线程同时进去 if-else 判断的可能性,借助 vmlens 这个插件,能够很明显看到原因,图如下:

图中可以看到在执行 ConcurrentHashMap 的原子操作 get 和 put 方法时候,出现了线程间的竞争,13 和 14 线程分别先获取到了对象的锁,然后取得了 map.get(1) 的值,此时值为 null,两个线程的取值都是 null,剩下的就比较明了了。两个线程都进入了 if-else 判断的第一个条件语句中,又先后复制 map.put(1,1),这样最终的结果 map.get(1).intValue() 就等于 1,断言失败。

欢迎有兴趣的童鞋一起交流

正文完
 0