java - 下面这段代码存在并发陷阱???
PHP中文网
PHP中文网 2017-04-18 10:10:31
0
2
547

曾宪杰的《大型网站系统与Java中间件实践》第一章第1.2.2.3小节给出以下代码示例:

使用HashMap数据被进行统计;

public class TestClass
{
    private HashMap<String, Integer> map = new HashMap<>();
    
    public synchronized void add(String key)
    {
        Integer value = map.get(key);
        if(value == null)
        {
            map.put(key, 1);
        }
        else
        {
            map.put(key, value + 1);
        }
    } 
}

使用ConcurrentHashMap保存数据并进行统计;

public class TestClass
{
    private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
    
    public void add(String key)
    {
        Integer value = map.get(key);
        if(value == null)
        {
            map.put(key, 1);
        }
        else
        {
            map.put(key, value + 1);
        }
    } 
}

使用HashMap时,对add方法加锁,此时该方法是线程安全的,为何换为ConcurrentHashMap之后,原书中说存在并发陷阱???

PHP中文网
PHP中文网

认证高级PHP讲师

모든 응답(2)
巴扎黑

ConcurrentHashMap으로 변경한 후 add 메소드를 잠가야 하는 이유는 무엇인가요? ? ?

으아아아

잠겨있지 않습니다.

편집:

실제로 동시성 함정이 있습니다. 다음 상황을 고려해보세요.

  1. 스레드 A는 map.get(key);if(value == null)을 실행하여 결과를 true로 얻은 다음 CPU 시간을 넘겨줍니다.

  2. 이때 스레드 B도 같은 위치에 실행되며 결과도 true입니다. 스레드 A는 아직 map.put(key, 1)을 실행하지 않았고 스레드 B는 map.put을 실행했기 때문입니다. (key, 1) , 이때 맵에는 이미 key 값이 있습니다.

  3. 스레드 A는 실행을 계속하기 위해 CPU 시간을 얻습니다. 이전 판단 결과가 true였기 때문에 스레드 A가 다시 실행됩니다. 최종 결과는 두 스레드가 모두 map.put(key, 1)을 한 번 실행한다는 것입니다. 이때 key의 값은 여전히 ​​1이지만 실제로는 2여야 합니다.

이 문제가 발생하는 이유는 ConcurrentHashMap의 단일 작업이 원자적이지만 외부 호출은 원자적이지 않기 때문입니다. map.get과 map.put은 서로 독립적인 두 작업이므로 스레드를 보장하려면 안전을 위해서는 get 및 put 작업의 원자성을 보장하기 위해 코드에 잠금을 추가해야 합니다.

左手右手慢动作

ConcurrentHashMap은 HashMap이 할 수 없는 동시 조건 하에서 내부 데이터가 일관성을 유지할 수 있다는 것만 보장합니다.

그러나 add 메소드는 일반적인 Check-Then-Act 또는 Read-Modify-Write이므로 스레드로부터 안전하지 않습니다.

클래스의 모든 필드가 스레드로부터 안전한 클래스인 경우 이 클래스가 스레드로부터 안전한지 여부에 대해 이 질문에 대해 생각해 볼 수 있습니다. 대답은 분명히 '아니요'입니다.

최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿