선생님, 개발을 잘하고 싶어요.

자바 병렬 프로그래밍 - 3부 10장 - 활동성을 최대로 높이기 본문

일상/책 리뷰

자바 병렬 프로그래밍 - 3부 10장 - 활동성을 최대로 높이기

알고싶은 승민 2022. 7. 3. 13:30
  • 안전성(safety)과 활동성(liveness)의 상관 관계
  • 활동성에 문제가 되는 상황을 알아보자.
  • 문제가 되는 사항을 미연에 방지하는 방법을 알아보자.

데드락

  • 식사하는 철학자 문제
    • 원형 테이블에 둘러 앉은 철학자
    • 자기 왼쪽에 있는 젓가락을 집은 다음, 오른쪽 젓가락을 사용할 수 있을 때까지 기다린 후, 오른쪽 젓가락을 집어서 식사를 한다고 하자.
    • 모든 철학자가 자기가 확보한 왼쪽 젓가락을 놓지 않고 오른쪽 젓가락을 기다리다가 식사를 하지 못하게됨
  • JVM은 데이터베이스 서버와 같이 데드락 상태를 추적하는 기능 ❌
  • 데드락은 시스템에 부하가 걸리는 최악의 상황에서 모습을 드러낸다.

락 순서에 의한 데드락

  • 두 개의 스레드가 서로 다른 순서로 동일한 락을 확보하려 할 때 발생
  • 같은 순서로 락을 확보하도록 돼 있다면, 종속성 그래프에 사이클 ❌ 👉 데드락 ❌
  • 🌟 모든 스레드에서 락을 모두 같은 순서로만 사용한다면 락 순서에 의한 데드락 ❌

동적인 락 순서에 의한 데드락

  • 함수의 인자로 들어온 객체를 대상으로 락을 건다면?
fun transferMoney(from: Account, to: Account) {
  synchronized(from) {
    synchronized(to) {
      // 함수 인자를 통해서 락 순서가 동적으로 변경
    }
  }
}

// 다음의 경우, 락 순서 싸이클이 생기고 데드락 발생 가능성
transferMoney(a, b)
transferMoney(b, a)
  • Systen.identityHashCode 등의 메서드를 활용해 락 순서를 고정할 필요가 있다.
  • 🌟 인자로 들어오는 객체, 혹은 동기화 객체 순서를 정렬해서 균일한 락 순서를 가지도록 해야한다.

객체 간의 데드락

class Taxi {
  val dispatcher: Dispatcher  

  @Synchronized
  fun getLocation(): Point

  @Synchronized // 1. Taxi 객체의 락을 확보
  fun setLocation(...) { 
    dispatcher.notifyAvailable() // 2. Dispatcher 객체의 락을 확보
  } 
}

class Dispatcher {
  @Synchronized // 1. Dispatcher 객체의 락을 확보
  fun notifyAvailable() { 
    taxi.getLocation() // 2. Taxi 객체의 락을 확보
  }
}

// 서로 다른 순서로 락을 가져가려는 상황 발생
  • 락을 확보하고 에일리언 매소드를 호출하는지 체크하기
  • 👀 에일리언 메소드 👉 정의는 되어있지만 기능이 만들어져 있지 않은 메소드, 내부에서 어떤 동작이 일어날 지 모르는 메소드

오픈 호출

  • 오픈 호출(open call) 👉 락을 전혀 확보하지 않은 상태에서 메소드를 호출하는 것
  • 오픈 호출을 활용하면, 활동성 분석도 간편
  • synchronized 블록도 범위를 최대한 줄이기
  • 🌟 프로그램 작성할 때 최대한 오픈 호출한다. 데드락 분석이 쉬워진다.

리소스 데드락

  • 필요한 자원을 사용하기 위해 대기하는 과정도 데드락 ⭕️
  • 스레드 부족 데드락, 다른 작업 실행 결과를 사용해야 하는 작업이 있다면, 스레드 소모성 데드락 원인 되기 쉽다.

데드락 방지 및 원인 추적

  • 가능하다면 한 번에 하나의 락만 사용하게 개발
  • 락 사용 순서를 설계 단계부터 고려
  • 데드락 발생 가능성 여부 판단
    • 여러 개의 락 확보하는 부분 파악
    • 락을 지정된 순서에 맞춰 사용하도록 한다
    • 👉 오픈 호출 방법을 사용하면 이같은 분석 작업이 쉬워진다.

락의 시간 제한

  • Lock 클래스 등 명시적 락은 일정 시간 동안 락 확보 못하면 tryLock에서 오류 발생시킬 수 있다.
  • 에러를 받고 재시도하도록 할 수 도 있겠다.

스레드 덤프를 활용한 데드락 분석

그 밖의 활동성 문제점

  • 소모, 놓친 신호, 라이브락

소모(starvation)

  • 소모 👉 스레드가 작업 진행하는 데 꼭 필요한 자원을 영영 할당받지 못하는 경우
  • 자바에서 소모 상황이 발생하는 대부분의 원인
    • 스레드 우선 순위를 적절치 못하게 올리거나 내리는 부분
      • 🌟 스레드 우선 순위는 왠만하면 건드리지 말자. 활동성 문제를 일으킬 수 있고, 플랫폼 종속적이 되곤 한다.
    • 락을 확보한 채 종료되지 않는 코드 실행 시 👉 다른 스레드는 해당 락을 영영 가저갈 수 없어 소모 상황 발생

형편 없는 응답성

  • 응답성이 떨어지는 상황

라이브락(livelock)

  • 라이브락 👉 실패할 수밖에 없는 기능을 계속해서 재시도하는 경우
    • e.g. 메시지 전송에 실패했을 때, 해당 트랜젝션을 롤백하고 큐의 맨 뒤에 쌓아두는 애플리케이션에서 발생
  • 회복 불가능한 오류를 회복 가능하다고 판단해 계속 재시도하는 과정에서 나타나는 문제
  • 각 스레드가 다른 스레드 응답에 따라 각자의 상태를 계속 변경하느라 실제 작업은 전혀 못하는 경우에도 발생
  • 임의의 시간 동안 기다리다가 재시도 하는 방법 등으로 해결

요약

  • 락 순서에 의한 데드락이 가장 흔한 활동성 문제
  • 프로그램을 개발할 때 오픈 호출 방법을 사용할 것을 고민해보자.
Comments