어플리케이션을 효율적으로 실행하기 위해 멀티코어를 온전히 활용하도록 구현하는 방식
(외부 서비스의 응답을 기다리면서 아무일도 하지 않으면 CPU 사이클이 낭비된다.)
* 대기 시간이 아주 길어 여러 스레드가 프로세서를 공유할 수 있거나, 여러 프로세스가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 높아짐
* Ex) 웹 브라우저에서 여러 가지 이미지 리소스들을 불러와 다운로드할 때
* 단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다.
* '무엇'과 '언제'를 분리하면 시스템의 구조가 크게 달라진다.
* 어플리케이션이 컨테이너를 통해 멀티 쓰레드를 사용하는 것이기 때문에 컨테이너의 동작을 이해해야 한다.
* 동시 수정, 데드락 같은 문제를 피할 수 있는지를 알아야 한다.
* 요청이 들어오면 Thread Pool에 있는 Thread가 서블릿의 service() 메서드 호출
* service doGet(), doPost()에서 요청에 대한 처리를 하도록 구현
* 동시성 코드는 독자적인 개발, 변경, 조율 주기가 있다.
* 동시성 코드에는 독자적인 난관이 있다. 다른 코드에서 겪는 난관과 다르며 훨씬 어렵다.
* 잘못 구현한 동시성 코드는 별의별 방식으로 실패한다.
* 동시 수정 문제를 피하기 위해 객체를 사용하는 코드 내 임계영역을 sychronized 키워드로 보호
* 보호할 임계영역을 뺴먹거나, 모든 임계영여억을 보호했는 지 확인하느라 수고가 드므로 임계 영역의 수를 최소화 해야함
* 서블릿처럼 각 Thread는 클라이언트 요청 하나를 처리한다.
* 모든 정보는 비공유 출처(client의 request)에서 가져오며 로컬 변수에 저장한다.
* 각 서블릿은 마치 자신이 독자적인 시스템에서 동작하는 양 요청을 처리한다.
* Thread Safe한 컬렉션을 사용(ConcurrentHashMap, AtomicLong)
* 서로 무관한 작업을 수행할 때는 executor 프레임워크를 사용
* 가능하다면 Thread가 Blocking 되지 않는 방법을 사용
* 클라이언트에서 잠금 - 클라이언트에서 첫 번째 메서드를 호출하기 전에 서버를 잠근다
* 서버에서 잠금 - 서버에다 "서버를 잠그고 모든 메서드를 호출한 후 잠금을 해제하는" 메서드를 구현
* 연결(Adapter) 서버 - 잠금을 수행하는 중간 단계를 생성
* 문제를 노출하는 테스트 케이스를 작성하라
* 프로그램의 설정과 시스템 설정과 부하를 바꿔가며 자주 돌려라
* 테스트가 실패하면 원인을 추적하라
* 다시 돌렸더니 통과한다는 이유로 그냥 넘어가면 절대 안 된다.
*코드에 wait(), sleep(), yield(), priority() 함수를 직접 구현
* 보조코드를 넣어주는 도구를 사용해 테스트
* 다양한 위치에 ThreadJigglePoing.jiggle()을 추가해 무작위로 sleep(), yield()가 호출되도록 한다.
* 테스트 환경에서 보조 코드를 돌려본다.