alt + insert
: generateThread Safe
주요 차이점은 ConcurrentHashMap은 내부적으로 동기화되어 스레드로부터 안전하다는 것. HashMap은 Collections.synchronizedMap() 메서드를 사용하여 외부적으로 동기화할 수 있다.
Internal Structure
ConcurrentHashMap의 모든 작업이 동기화되는 것은 아니다. 추가 및 삭제와 같은 수정 작업만 동기화되고 읽기 작업은 동기화되지 않는다.
HashMap을 Collections.synchronizedMap() 메서드를 사용하여 외부에서 동기화하는 경우, 모든 작업이 동기화 된다. 이렇게 하면 속도가 느려지는 문제가 있다.
Introduction Into Java Collection Framework
HashMap은 JDK 1.2 부터 Java Collection Framework에 포함되었고, ConcurrentHashMap은 나중에 Java Collection Framework에 동시성 패키지의 일부로 도입 되었음. ConcurrentHashMap은 레거시 클래스인 HashTable의 대안으로 취급된다. (HashTable 클래스의 메서드에는 synchronized 키워드 사용하고 있어 스레드 세이프하지만, 속도 저하 이슈가 있음.)
Null Keys And Null Values
HashMap은 하나의 null 키와 여러 null 값을 허용한다.
ConcurrentHashMap은 null 키와 null 값을 허용하지 않는다.
Fail-Fast Vs Fail-Safe
HashMap의 Iterator는 fail-fast 방식을 사용. Iterator가 생성된 이후 Map이 수정되면 즉시 ConcurrentModificationException 예외가 발생하여 프로그램이 중단된다.
ConcurrentMap의 Iterator는 fail-safe 방식을 사용. Iterator가 생성된 이후 Map이 수정되어도 예외가 발생하지 않는다. Iterator가 반환하는 값은 Iterator가 생성된 시점에서의 Map 상태를 기반으로 하기 때문에 항상 일관된 값을 반환한다. 즉, 수정된 값을 반영하지 않는다.
Performance
ConcurrentHashMap의 수정 작업만 동기화 된다. 따라서 ConcurrentHashMap의 추가 또는 삭제 작업은 HashMap 보다 느리다.
읽기 작업은 HashMap과 ConcurrentHashMap 모두 동기화하지 않기 때문에 동일한 성능을 보인다.
When To Use What?
HashMap은 내부에서 동기화를 제공하지 않기 때문에 단일 스레드(single-threaded) 애플리케이션에 적합하다.
ConcurrentHashMap은 내부에서 동기화(synchronization)를 제공하여 여러 개의 스레드가 동시에 맵에 접근하고 수정할 수 있도록 하기 때문에 동시 다중 스레드(concurrent multi-threaded) 애플리케이션에 적합하다.
interface명+impl
이라는 명을 conventinal하게 붙인다.public class OrderServiceImpl implements OderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
OderService를 기준으로 봤을 때 사실 discountPolicy.discount는 뭐 사실 내 알빠아니고 그냥 interface의 .discount
를 implement한 너의 로직의 결과만 내놔! 라는게 되기 때문에 추후 할인 정책이 바뀌어도 어차피 discount 메서드는 살아있을 것이고 정책에 해당하는 로직만 바꿔서 치환해주면 되기 때문에 결합도가 떨어져서 단일 책임원칙을 잘 지킨 예라고 볼 수 있다.
참고로 저기 discount에서 현재 필요한 로직은 VIP 즉, Grade밖에 없지만 미래 확장성
때문에 member 데이터를 통으로 넘긴 것이다.
ctrl + shift + T
, mac : cmd + shift + T
그리고 이때 JUnit 5(23.08.30)를 사용하는 것이 좋다.
또한 성공테스트도 중요하지만 실패 테스트 케이스도 다양하게 해보는 것이 매우 중요하다.
opt + enter
, window : alt + endter
로 static하게 import하는 것이 훨씬 편리하고 개발속도를 조금 올려줄 수 있다.대충 읽어보니 영한쌤 강의는 JAVA 11에 의존한다고 하였는데 시간이 지나 스프링이 업데이트 되면서 18년도 구시대 JAVA11은 호환이 안 된다고 적혀있었다.
하지만 걱정말라 Gradle의 자바 버츄얼 머신을 업그레이드해주면 된다. 아래와 같에 셋팅에서 버전을 다시 잡아주고
단축키(window: ctrl alt s, mac: via cmd)
Build and Run 부분을 모두 Gradle -> IntelliJ로 바꿔주면 intelli가 gradle을 통하여 실행하는 것을 intelli가 바로 실행하기 때문에 조금 더 빠르다고 한다.