https://velog.io/@jay13jeong/동시성-처리하기
기존 코드는 synchronized 사용하면서 스레드마다 병목이 생겨서 유저가 많아질 수록 응답시간이 매우 낮아 졌습니다.
ConcurrentHashMap는 Lock Striping이라는 분할 잠금방식을 채택하고 있습니다. 공유자원을 완전히 잠그지 않고 병렬로 일부만 잠가서 성능을 높인 자료구조입니다.
이번엔 synchronized블록을 제거하고 ConcurrentHashMap의 add가 성공하면 true 실패하면 false를 반환하는 특징을 사용하여 최적화를 해보겠습니다.
private final Database db;
private final static Set<Long> depositingUsers = ConcurrentHashMap.newKeySet();
public Mileage postMileage(Long id, Long mileage) { Mileage result; if (!depositingUsers.add(id)){ return null; } result = db.postMileage(id, mileage); try { Thread.sleep(10000L); } catch (Exception e){ //err logger return null; } finally { depositingUsers.remove(id); } return result; }
테스트 방식은 이전과 동일합니다.
이번에도 ExecutorService와 CountDownLatch를 사용하여 테스트 하였습니다.
측정 시간은 스레드를 모두 생성 후 Latch를 풀기 직전부터 스레드가 모두 종료 순간까지 입니다.
오차범위는 500ms입니다.
기대값은 각 유저당 요청 1개 성공과 나머지 실패입니다.
Deduplication을 확실하게 하기위해서 스레드 대기시간을 10000ms까지 늘렸습니다.
작은 테스트에서는 시간이 조금 늘어 났지만 큰 규모에서 보았을 때 확실히 성과가 나타났습니다.
Lock Striping기능을 적극 활용하면서 성능이 기존에 비해 2배이상 최적화 되었습니다.
테스트 유저수가 많아질수록 체감이 더 커집니다.