Post

자바 Thread_12


블로킹 IO

  • 공유 자원에서 경합하는 두 개의 스레드가 있는 경우
  • 스레드1이 잠금을 얻으면 스레드2는 차단당하고 대기해야함
    그러면 OS는 스레드2 예약을 취소하고 잠금이 해제되면 다시 예약함
    => 시간이 짧더라도 성능에 영향을 줌




IO란?

  • 컴퓨터 구조는 CPU, 메모리가 함께 있고 키보드, 마우스, 모니터, 하드 등 주변장치는 외부장치로 간주함
  • 이런 이유는 cpu가 언제든지 메모리에 직접 접근할 수 있기 때문
  • 프로그램은 운영체제 개입 없이 메모리에서 변수를 읽거나 쓸수있음
  • 그러나 CPU는 직접 하드웨어에 접근할 수 없음. => 장치마다 있는 컨트롤러와 통신해 데이터를 송수신해야함.

  • CPU에 인터럽트가 발생하면 장치에서 작업 결과를 읽는데 관여함
    그리고 컴퓨터는 DMA를 통해 CPU 개입없이 메모리에 읽기,쓰기 권한을 장치에 부여할 수 있음 (프로세스 실행가능)
  • 통신이 초기화되거나 준비된 데이터가 인터럽트를 발생하면 그 때 CPU가 관여함


  • 스레드풀을 유지함으로 애플리케이션 전체 기간동안 같은 스레드를 재사용할 수 있음. => 새 작업이 있는 동안 스레드를 만들고 시작하고 종료하는 부하를 줄여줌. 그리고 최적의 크기는 컴퓨터의 코어수가 동일하게 설정하면 됨
  • 그러면 OS가 각 스레드를 별도의 코어에 예약하고 해당 스레드를 완전히 병렬로 실행하기에 하드웨어를 완전히 활용가능함.
  • 중요한것은 항상CPU에서 실행되는 작업만 포함됨(블로킹 호출X)






온라인 상점 웹 애플리케이션의 시나리오를 살펴볼게요

  • 웹 애플리케이션은 각자 다른 상품 카테고리에 관심을 보이는 여러 사용자에게서 들어오는 HTTP 요청을 수신합니다
  • 요청을 받으면 고객 요청을 읽은 다음 데이터베이스에 연결해 현재 가능한 제품 목록과 리뷰, 가격을 읽어옴
  • 데이터베이스에서 정보를 다 받으면 웹 페이지로 정보를 다시 보내 사용자가 볼 수 있게 합니다
    • 간단하게 하기 위해 이 애플리케이션이 단일 코어 컴퓨터에서 돌아간다고 해봅시다
    • 스레드의 수를 코어 수와 동일하게 유지하는 정책을 그대로 따른다면 우리 스레드는 한 개가 되겠죠
    • 클라이언트의 요청을 파싱하고 사용자에게 응답을 보내주는 것은 간단합니다
  • 반면 다른 컴퓨터에서 실행되는 원격 데이터베이스에 연결해서 복잡한 쿼리가 완료되기를 기다리는 데는 꽤 오랜 시간이 걸릴 겁니다
  • 나아가 스레드가 차단된 동안 블로킹 IO 연산이 일어나며 이는 CPU 유휴 상태라고 함
    => 이런 유형의 애플리케이션을 IO 바운드 애플리케이션이라고 합니다, 왜냐하면 대부분의 시간을 CPU 사용에 소모하기보다 IO에 소모하기 때문입니다
  • 이 시간 동안 새로운 요청이 들어오면 코어가 하나만 있더라도 사실 CPU에는 여유가 있습니다
  • 하지만 하나의 스레드만 허용하고 긴 IO 연산 때문에 차단된 상태라 새로운 요청은 첫 번째 요청이 끝날 때까지 네트워크 카드 큐에 들어가게 됩니다
  • 이러한 IO 바운드 애플리케이션은 산업에서 매우 흔히 보이는 유형임


대부분의 웹사이트를 구성하는 웹 애플리케이션은 사실 IO 바운드 애플리케이션

  • 대부분의 시간을 사용자 요청에 응답이 오기를 기다리는 데 사용
  • 원격 서비스나 데이터베이스에서 오거나, HTML, CSS, JavaScript를 로컬 디스크에서 불러오는 것


빅데이터 처리 및 트랜스포메이션 애플리케이션입니다

  • 이러한 애플리케이션은 데이터베이스에 저장할 데이터를 정제하고 준비하거나, 머신 러닝을 합니다
  • 그리고 대부분의 시간을 큰 파일을 디스크에서 읽거나 다른 트랜스포메이션 애플리케이션에 결과를 전송하거나 다른 위치에 저장하는 데 필요한 IO에 사용합니다
  • 하지만 IO 바운드 애플리케이션에서만 블로킹 IO가 문제가 되는 것은 아닙니다. 블로킹 IO의 문제점을 살펴보면 가끔씩 일어난다 해도 다른 시나리오가 있음


멀티스레드 환경 온라인 상점 웹 애플리케이션(4코어 4스레드 가정)

  • 하지만 이번에는 메모리에 일부 데이터를 저장해 두고 가끔 필요한 데이터가 메모리에 없을 때만 데이터베이스에 외부 호출을 합니다
  • 시간 순으로 보면 첫 번째로 4명의 서로 다른 사용자에게서 4개의 요청을 받음
  • 이중 3명은 애플리케이션 메모리에서 필요한 데이터를 찾았어요 그래서 짧은 시간에 작업을 완료했고 요청 하나만이 애플리케이션이 데이터베이스에 요청을 해야 합니다, 긴 시간이 걸리겠죠
  • 이제 두 번째로 4명의 다른 사용자에게 4개의 요청이 들어왔습니다 하지만 지금은 4개가 아닌 3개 스레드만 사용 가능합니다, 왜냐하면 첫 번째 스레드는 이 IO 연산 때문에 여전히 차단된 상태입니다
  • 비슷하게 이중 3개의 요청은 외부 호출 없이 완료되었다고 가정하고 배치 중 네 번째 호출만이 데이터베이스에서 데이터를 가져와야 한다고 해봅시다
  • 이제 두 개의 스레드가 IO로 차단되었고 향후 요청을 처리할 스레드는 두 개밖에 없음. 요청을 처리할 수 있는 스레드는 빠르게 줄어들고 1개가 아니라 4개의 CPU 코어가 있어도 아무것도 하지 못함
  • IO 바운드 애플리케이션의 앞선 시나리오와 달리 이 경우에는 요청 중 몇 개만 처음에 데이터베이스 연결이 필요하죠


예시를 통해 알 수 있는 점

  1. 우리 작업이 어떤 유형의 블로킹 호출을 포함할 경우 코어 수만큼 최대한 스레드 풀을 가동해도 CPU를 최대로 활용하지 못하고 최선의 성능을 내지 못합니다
    • CPU는 대부분의 시간을 유휴 시간으로 사용했습니다
  2. 블로킹 호출이 몇 개 없더라도 전체 애플리케이션 성능에 영향을 주며 블로킹 호출이 필요한 작업뿐 아니라 모든 호출이 영향을 받게 됩니다
    • 몇 개의 요청만이 원격 데이터베이스 접근이 필요했는데도 다른 사용자의 요청도 CPU를 점유하지 못하게 막아버렸어요



  • IO 연산 시간 동안 CPU은 관여하지 않으며 다른 작업이 있다면 수행할 수 있는 상태

  • 블로킹 호출을 포함하는 작업을 수행할 때 코어 수만큼 스레드를 만드는 정책을 따라도 하드웨어 활용률이나 성능을 최적화하지 못함


출처 - https://kmooc.udemy.com/course/java-multi-threading/ (Java 멀티스레딩, 병행성 및 성능 최적화 - 전문가 되기 )

This post is licensed under CC BY 4.0 by the author.