[CleanCode 부록A] 동시성 2

soyeon·2022년 8월 24일
0

CleanCode

목록 보기
18/18
post-thumbnail

클라이언트/서버 예제

서버는 소켓을 열어놓고 클라이언트가 연결하기를 기다린다. 클라이언트는 소켓에 연결해 요청을 보낸다.
-> 주로 시간을 보내는 곳 : I/O(소켓 사용, 데이터베이스 연결, 가상 메모리 스와핑 기다리기), 프로세서(수치 계산, 정규 표현식 처리, 가비지 컬렉션)
둘 중 하나가 지배적으로 차지하고 있다. 프로세서에서 시간을 보내면 하드웨어를 추가해 성능을 올리면 된다. I/O 연산에서 시간을 보내면 동시성이 성능을 높여준다.

결론 : 단일스레드 시스템을 다중스레드 시스템으로 변환해 시스템 성능을 높인다.

가능한 실행 경로

  • incrementValue 메서드
public class IdGenerator {
	int lastIdUsed;
    
    public int incrementValue() {
    	return ++lastIdUsed;
    }
}

메서드에서 반환값은 lastIdUsed 값과 동일하다. 하지만 스레드 두개로 실행하면 다양한 결과가 나오게 된다.
=> synchronized로 선언하면 문제가 해결된다.

경로 수

스레드가 N개이면 가능한 경로 수는 N!이다.

심층 분석

원자적 연산 : 중단이 불가능한 연산 (ex. 메모리에 값을 할당하는 연산)
전처리 증가 연산자(++)는 중단이 가능하다.

라이브러리를 이해하라

Executor 프레임워크

: 스레드 풀을 관리하고, 풀 크기를 자동으로 조정하며, 필요하다면 스레드를 재사용한다.

스레드를 차단하지 않는 방법

락을 거는 쪽보다 문제를 감지하는 쪽이 거의 항상 효율적이다.

다중 스레드 환경에서 안전하지 않은 클래스

  • SimpleDateFormat
  • 데이터베이스 연결
  • java.util 컨테이너 클래스
  • 서블릿

메서드 사이에 존재하는 의존성을 조심하라

스레드 두 개가 인스턴스 하나를 공유하게 되면 예외가 발생할 가능성이 있다.

해결 방안
1. 실패를 용인한다.
때로는 실패해도 괜찮도록 프로그램을 조정할 수 있다.
2. 클라이언트-기반 잠금
각 클라이언트는 synchronized 키워드를 이용해 객체에 락을 건다. 하지만 서버를 사용하는 모든 프로그래머가 락을 기억해 객체에 걸었다 풀어야 하기 때문에 위험하다.
3. 서버-기반 잠금
다중 스레드를 고려하게 클라스 API를 설계한다.
=> 코드 중복이 줄어든다. 성능이 좋아진다. 오류가 발생할 가능성이 줄어든다. 스레드 정책이 하나이다. 공유 변수 범위가 줄어든다.

작업 처리량 높이기

다중스레드 환경이 단일스레드 환경과 비교해 처리율이 세 배가 높다.

데드락

어떠한 애플리케이션이 생선과 갱신이라는 연산 두개를 수행한다.
만약, 풀 크기가 사용자 수가 많다면 데드락이 발생하게 된다.

해결책
: 디버깅 문을 추가해 사태를 파악해야 한다.

데드락 발생 원인

아래 네 조건을 모두 충족해야 데드락이 발생한다.

상호 배제

여러 스레드가 한 자원을 공유하나 그 자원은 여러 스레드가 동시에 사용하지 못하며 개수가 제한적일 때 상호 배제 조건을 만족한다.

잠금&대기

한 스레드가 자원을 점유하면 작업을 마칠 때까지 내놓지 않는다.

선점 불가

스레드는 다르 스레드에게 자원을 빼앗지 못한다.

순환 대기

스레드가 점유하고 필요한게 얽혀서 사용할 수 없는 경우이다.

상호 배제 조건 깨기

동시에 사용해도 괜찮은 자원을 사용한다. 스레드 수 이상으로 자원 수를 늘린다. 자원을 점유하기 전에 필요한 자원이 모두 있는지 확인한다.

잠금&대기 조건 깨기

자원을 점유하기 전에 어느 하나라도 점유하지 못하면 지금가지 점유한 자원을 몽땅 내놓고 처음부터 시작한다.
기아와 라이브락이 발생할 수 있다.

선점 불가 조건 깨기

다른 스레드로부터 자원을 뺏어온다.

순환 대기 조건 깨기

스레드에 자원을 똑같은 순서로 할당하게 만든다.
모든 스레드가 일정 순서에 동의하고 그 순서로만 자원을 할당한다.

0개의 댓글