μžλ°” μ‹ μž… 개발자 λ©΄μ ‘ 질문 쀑에 λ©€ν‹°μŠ€λ ˆλ“œμ™€ λ™μ‹œμ„± λ¬Έμ œλŠ” λŒ€λΆ€λΆ„μ˜ νšŒμ‚¬μ—μ„œ 단골 μ§ˆλ¬Έμ— ν•΄λ‹Ήν•œλ‹€. 쑰직의 μ£Όλ‹ˆμ–΄ κ°œλ°œμžκ°€ λ©€ν‹°μŠ€λ ˆλ“œλ₯Ό κ³ λ €ν•˜μ§€ μ•Šκ³  λΉ„μ¦ˆλ‹ˆμŠ€ μš”κ΅¬μ‚¬ν•­μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ μ½”λ“œλ₯Ό μˆ˜μ •ν•œ 결과둜 인해 μ‹œμŠ€ν…œμ—μ„œ μ£Όμš” κΈ°λŠ₯ 쀑 ν•˜λ‚˜μΈ 이벀트 λͺ¨λ‹ˆν„°λ§ ν™”λ©΄μ—μ„œ 데이터가 κ°„ν—μ μœΌλ‘œ μ˜¬λ°”λ₯΄μ§€ μ•Šμ€ λ¬Έμ œκ°€ λ°œμƒν•¨μ΄ 리포트 λ˜μ—ˆλ‹€.

λ™μ‹œμ„± λ¬Έμ œκ°€ λ°œμƒν•˜λŠ” μ½”λ“œ

λ©€ν‹°μŠ€λ ˆλ“œμ— μ˜ν•œ 병렬 처리 μ‹œ λ™μ‹œμ„± λ¬Έμ œκ°€ λ°œμƒν–ˆλ˜ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ½”λ“œλŠ” λŒ€μΆ© μ•„λž˜μ™€ κ°™λ‹€κ³  λ³Ό 수 μžˆλ‹€.

List<Customer> customers = IntStream.rangeClosed(1, 100)
                .mapToObj(i -> new Customer().setId(String.valueOf(i)))
                .toList();

List<String> optIds = new ArrayList<>();
customers.parallelStream().forEach(customer -> {
    optIds.add(customer.getId());

    // customer 에 λŒ€ν•œ 데이터 쑰회 둜직
});
System.out.println("expected: 100, size: " + optIds.size());

μœ„ μ½”λ“œμ—μ„œ ArrayList 에 μ €μž₯λ˜λŠ” λͺ©λ‘μ˜ κ²°κ³Όκ°€ 맀번 동일함이 보μž₯이 λ˜λŠ”κ°€? κ°€ μ€‘μš”ν•œ 뢀뢄인데 μ‹€μ œλ‘œ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό 돌렀보면 κ°„ν—μ μœΌλ‘œ 100κ°œκ°€ μ•„λ‹Œ 97κ°œκ°€ λͺ©λ‘μ— ν¬ν•¨λ˜λŠ” 경우λ₯Ό 확인할 수 μžˆμ„ 것이닀. λ©€ν‹°μŠ€λ ˆλ“œμ— μ˜ν•΄μ„œ add ν•¨μˆ˜κ°€ λ™μ‹œμ— ν˜ΈμΆœλ˜λŠ” κ²½μš°μ—λŠ” ConcurrentModificationException와 같은 μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”κ²Œ μ€‘μš”ν•œ 뢀뢄이닀.

ArrayList 와 HashSet에 λŒ€ν•œ 동기화 μ»¬λ ‰μ…˜

일반적으둜 많이 μ‚¬μš©λ˜λŠ” ArrayList κ³Ό HashSet 에 데이터λ₯Ό μΆ”κ°€ν•  λ•Œ ParallelStream 을 μ‚¬μš©ν•˜λŠ” 경우 μœ„μ™€ 같이 λ™μ‹œμ„± 문제둜 인해 μ œλŒ€λ‘œ μΆ”κ°€λ˜μ§€ μ•ŠλŠ” ν•­λͺ©μ΄ μ‘΄μž¬ν•  수 μžˆλ‹€. 이둜 μΈν•œ λ™μ‹œμ„± 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ•„λž˜μ™€ 같이 Synchronized ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ 동기화 문제λ₯Ό κ³ λ €ν•œ μ»¬λ ‰μ…˜ ν΄λž˜μŠ€λ“€μ΄ μ‘΄μž¬ν•œλ‹€.

  • ConcurrentSkipListSet
  • CopyOnWriteArrayList
  • Collections.synchronizedList();
  • CopyOnWriteArraySet
  • Collections.synchronizedSet();
  • ConcurrentHashMap.newKeySet();

λ™μ‹œμ„± λ¬Έμ œκ°€ λ°œμƒν–ˆλ˜ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—μ„œ CopyOnWriteArrayList 와 CopyOnWriteArraySet은 μ“°κΈ° μž‘μ—…μ— λŒ€ν•œ μ˜€λ²„ν—€λ“œκ°€ μžˆλ‹€λŠ” 점을 κ³ λ €ν•˜μ—¬ Collections.synchronizedList λ₯Ό μ‚¬μš©ν•˜λŠ” κ²ƒμœΌλ‘œ κ²°μ •ν•˜μ—¬ μˆ˜μ •ν•˜μ˜€λ‹€.

μžλ°”μ—μ„œ λ³‘λ ¬μ²˜λ¦¬ μ‹œ λ™μ‹œμ„±μ„ κ³ λ €ν•΄μ•Όν•˜λŠ” 것은 기초적인 뢀뢄에 ν•΄λ‹Ήλ˜μ–΄ μ£Όλ‹ˆμ–΄ κ°œλ°œμžμ—κ²Œ μ•„μ‰¬μš΄ 뢀뢄이긴 ν•˜λ‚˜ μ΄λŸ¬ν•œ 문제λ₯Ό λ‚΄μž¬ν•˜κ²Œ 된 κ°€μž₯ 큰 원인은 μš”κ΅¬μ‚¬ν•­μ„ μ²˜λ¦¬ν•œ λ‹Ήμ‹œμ— 개발 λ¦¬λ“œλ‘œμ¨ μ½”λ“œ 리뷰λ₯Ό μƒμ„Έν•˜κ²Œ 해주지 μ•Šμ•˜λ˜ κ²ƒμ΄λ―€λ‘œ 슀슀둜 λ°˜μ„±ν•΄μ•Όν•  뢀뢄이라 μƒκ°λ©λ‹ˆλ‹€.