java multi-thread programming

최창효·2022년 2월 25일
0

자바 이해하기

목록 보기
5/8
post-thumbnail

병렬 처리

여러 개의 작업을 동시에 실행하는 것을 말합니다.
동시에 해결할 수 있는 부분을 분산처리 함으로써 처리의 부하를 분담하고, 작업 시간을 단축시키는 방법입니다.

동기 처리와 비동기 처리

  • 동기 처리: 요청을 보낸 후 응답을 받아야만 다음 동작이 진행되는 처리방식 입니다.
  • 비동기 처리: 요청을 보낸 후 응답을 기다리지 않고 다른 작업을 실행하고 있는 처리방식 입니다.

동기화(Synchronization)

  • 여러 개의 Thread가 한 개의 자원을 사용하고자 할 때,해당 Thread를 제외한 나머지는 접근을 못하도록 막는 걸 말합니다.
  • 동기화를 통해 race condition,deadlock등의 문제를 해결할 수 있습니다.

멀티 스레드

병렬처리 방법 중 하나입니다.
하나의 프로세스에서 다수의 스레드를 사용해 동시에 여러 작업을 처리하는 걸 멀티 스레드(멀티 스레딩)이라고 합니다.

멀티 스레드를 동작시키는 방식 - 동시성과 병렬성

  • 동시성(Concurrency)

    • 싱글 코어에서 멀티스레드를 동작시키기 위한 방식으로, 멀티 태스킹을 위해 여러 개의 스레드가 '번갈아가면서' 실행되는 성질입니다.
    • 동시에 실행되는 '것처럼 보이게'되는 걸 동시성이라 합니다.
    • 콘텍스트 스위칭이 발생합니다.
  • 병렬성(Parallelism)

    • 멀티 코어에서 멀티스레드를 동작시키는 방식으로, 한 개 이상의 스레드를 포함하는 각 코어들이 '동시에' 실행되는 성질입니다.

프로세스

  • 프로세스는 실행중인 프로그램을 말합니다.
  • 프로세스는 stack, heap, data, code로 이루어져 있습니다.
  • 프로그램이 프로세스가 되는 과정
    • Data,Code,Heap,Stack이 메모리에 올라가 메모리 공간을 확보합니다.
    • 해당 프로세스에 대한 정보를 담고 있는 PCB가 프로세스 생성과 함께 만들어집니다.

스레드

  • 스레드는 프로세스 내에서 실제로 작업을 수행하는 주체입니다.

많은 사람들이 프로세스는 공장, 스레드는 공장에서 일하는 일꾼으로 비유하곤 합니다.

멀티 스레드의 장점

  • 멀티 스레드는 자원활용의 효율성이 좋습니다.
    • 메모리를 공유하기 때문에 메모리 절약성이 높습니다.
  • 각각의 멀티 스레드는 하나의 Method Area와 Heap Memory를 공유하고 있기 때문에 통신의 부담이 적습니다.
    • cf)프로세스간의 자원공유에는 IPC통신이 필요합니다.

멀티 스레드의 단점

  • 프로그램의 복잡도가 증가합니다.
  • 자원을 공유하기 때문에 한 곳에서의 잘못이 전체에 영향을 줄 수 있습니다.

멀티 프로세스와 멀티 스레드

  • 멀티 프로세스는 멀티 스레드보다 context switching비용이 비쌉니다.
    • 멀티 프로세스는 캐쉬 메모리 초기화 등의 무거운 작입이 진행됩니다.
    • context swtiching은 기존 프로세스의 context를 저장하고, 다음 프로세스를 진행할 수 있게 context를 교체하는 작업입니다.
    • 멀티 프로세스는 프로세스간 통신비용이 비쌉니다.
      • 프로세스간의 자원공유에는 IPC통신이 필요합니다.

멀티 스레드 환경 주의사항

race condition(경쟁 상태)

  • 여러 스레드가 공유자원에 동시에 접근하려 할 때 일어나는 문제입니다.
  • 'A스레드는 변수의 값을 변경하고 있는데 B스레드가 해당 변수를 사용사려고 하는 상황'이 경쟁 상태의 한 예시입니다.
  • 경쟁 상태가 존재하면 프로그래머가 설계한대로 코드가 작동하지 않을 가능성이 크기 때문에 이를 없애줘야 합니다.

해결방법 - 뮤텍스와 세마포어

  • 임계 영역 설정을 통해 두 자원이 동시에 접근하지 못하도록 막는 걸 상호 배제라고 합니다.
    • 뮤텍스세마포어도 상호 배제의 하나입니다.
    • 뮤텍스와 세마포어는 동기화(Synchronization)를 활용한 방법들입니다.
  • 상호 배제 이외에도 진행(progress)한계 대기(bounded waiting)등의 경쟁 상태 해결방법이 있습니다.
    • 진행: 임계 구역을 실행하고 있는 프로세스가 없을 때, 몇 개의 프로세스가 임계 구역에 진입하고자 하면 이들의 진입 순서는 이들에 의해서만 결정되어야 한다. 또한 이 선택은 무한정 연기되어서는 안된다.
    • 한계 대기: 한 프로세스가 자신의 임계 구역에 진입하고자 요청을 한 후부터 이 요청이 허용될 때까지 다른 프로세스가 그들의 임계 구역에 진입할 수 있는 회수가 제한되어야한다.

임계 영역(Critical Section)

임계영역은 lock을 걸어 다른 스레드가 동시에 접근하지 못하도록 설정한 구간입니다.

뮤텍스(Mutex)

공유 자원에 여러 스레드가 접근하는 것을 막는 것을 뮤텍스라고 합니다.

세마포어(Semaphore)

공유 자원에 여러 프로세스가 접근하는 것을 막는 것을 세마포어라고 합니다.

뮤텍스와 세마포어의 차이

  • 스레드의 접근을 막는 뮤텍스, 프로세스의 접근을 막는 세마포어
  • 뮤텍스는 동기화 대상이 오직 1개일때만 사용가능, 세마포어는 동기화 대상이 여럿일때 사용가능
  • 뮤텍스는 오직 해당 스레드에서만 lock 해제가능, 세마포어는 다른 스레드에서도 lock 해제가능

뮤텍스와 세마포어는

  • 하나의 서버에서 여러 개의 프로세스, 스레드가 존재할 때 활용 가능한 Lock입니다. 대용량 트래픽을 처리하기 위해 여러 서버를 사용할 때는 뮤텍스와 세마포어를 활용할 수 없습니다.

deadlock(교착상태)

  • 교착상태란 둘 이상의 프로세스(스레드)가 서로의 작업이 끝나기만을 기다리고 있어 모두가 영원히 끝나지 않는 상황을 말합니다.

다음 4가지가 모두 존재할 때 교착상태가 발생할 수도 있습니다.(필요조건)

  • 상호배제 - 자원은 한 프로세스에 의해서만 점유 가능합니다.
  • 점유대기 - 프로세스가 할당된 자원을 가진 상태에서 다른 자원을 기다립니다.
  • 비선점 - 다른 프로세스가 점유한 자원은 강제로 빼앗을 수 없습니다.
  • 순환대기 - 각 프로세스는 순환적으로 다음 프로세스가 요구하는 자원을 가지고 있습니다.

동시에 접근하지도 못하며(상호 배제) 강제로 뺐을수도 없는데(비선점) 먼저 양보도 안하고(점유대기) 서로가 서로에게 필요한 걸 가지고만 있는(순환대기) 상황에서 교착상태가 발생합니다.

교착상태 해결방법

  • 교착 상태의 예방

    • 운영체제를 설계할 때 교착상태가 발생할 가능성을 없애는 방법입니다.
    • 예방방법은 자원 사용의 효율성이 떨어지고 비용이 많이 듭니다.
    • 위 4가지 조건 중 하나를 배제함으로써 교착상태 예방을 실천합니다.
  • 교착 상태의 회피

    • 자원할당을 신중히 해 순환대기를 피하는 방법입니다.
    • 대표 알고리즘
      • 자원 할당 그래프 알고리즘
      • 은행원 알고리즘
  • 교착 상태의 발견

    • 회피처럼 신중히 자원을 할당하는 게 아니라 일단 자원을 주고, 주기적으로 교착 상태가 발생하는지를 감시하는 방법입니다.
  • 교착 상태의 회복

    • 교착 상태를 발견했을 때 정상 상태로 되돌리는 방법을 말합니다.
    • 방법
      • 교착상태에 포함되어 있는 모든 프로세스들을 중지시킵니다.
      • 교착상태에 포함되어 있는 각 프로세스의 수행을 롤백시킵니다.
      • 교착상태가 없어질 때까지 교착상태에 포함되어 있는 프로세스들 하나씩 종료시킵니다.
      • 교착상태가 없어질 때까지 교착상태에 포함되어 있는 자원을 하나씩 선점시킵니다.

Thread-safe

  • Thread-safe란 멀티스레드 환경에서 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없는 상태를 말합니다.

스레드 안전성 보장받는 방법

  • lock을 사용합니다.
    • synchronized 키워드를 통해 동기화를 진행해 줍니다.
  • 동기화된 자료구조를 사용합니다.
    • concurrent 패키지에 있는 자료구조를 통해 안전성을 높일 수 있습니다.
  • 불변객체(immutable class)를 사용합니다.
    • final키워드를 통해 선언한 변수와 객체는 여러스레드가 동시에 접근해도 동일한 값을 보장받을 수 있기 때문에 thread-safe합니다.
    • 대표적인 불변객체는 String입니다.
  • 공유 자원의 사용을 최소화합니다.
    • 스레드별로 고유의 공간을 가지는 stack에 한정해 프로그래밍을 진행하면 안전성을 높일 수 있습니다.
  • Atomic클래스를 활용합니다.
    • non-blocking상태로 동기화 문제를 해결하는 방법입니다.
    • CAS(Compare And Sweep) 알고리즘으로 동작합니다.
      • 인자로 기존값과 변경할 값을 전달합니다.
      • 기존값이 현재 메모리에 있는 값과 동일하다면 변경할 값을 반영시켜 변경해 줍니다.
      • 기존값이 현재 메모리에 있는 값과 다르다면 변경하지 않고 돌려보냅니다.
    • thread의 상태를 변경시키지 않기 때문에 synchronized보다 성능이 좋습니다.
    • concurrent패키지들이 내부적으로 이러한 방법을 사용합니다.
  • Volatile키워드를 활용합니다.
    • volatile키워드가 붙어있는 객체는 CPU캐시가 아닌 메인 메모리(ram)에서 값을 참조합니다.
    • 비가시성: Cache에 담아쓰기 때문에 다른 스레드의 캐시에 있는 변수가 어떤 상태인지 알 수 없는 상황입니다.
      가시성이 보장되었다고 동시성이 보장되는 건 아닙니다.
      • 단, 한 스레드만 Write를 하고 나머지 스레드는 Read만 하는 상황에서는 가시성이 보장되면 동시성도 보장됩니다.
    • 과정
      • 멀티 스레드 환경에서는 스레드마다 각자의 CPU캐시에 변수값을 저장해 둡니다.
      • 메인 메모리에 변경된 변수를 캐시 메모리가 미처 반영하지 못했다면 캐시 메모리를 참조한 스레드는 잘못된 값을 가지게 됩니다.
      • 이를 방지하기 위해 스레드가 캐시 메모리가 아닌 메인 메모리의 값을 직접 읽고쓰게 만드는 게 바로 volatile키워드 입니다.
      • 직접 가져다쓰기 때문에 위와같은 문제가 발생하지 않습니다.

thread

thread 상태

  • getState()메서드를 통해 스레드 상태를 알 수 있습니다.
  • JVM은 스레드를 new,runnable,running,waiting,terminate다섯가지 상태로 관리합니다.

new

  • 스레드 객체가 생성 후, 아직 start() 메소드가 호출되지 않은 상태입니다.

runnable

  • 실행 상태로 언제든지 갈 수 있는 상태입니다.

running

  • 스케줄링으로 선택되어 CPU를 점유하고 run() 메소드를 실행한 상태입니다.

waiting

  • 스케줄링으로부터 선택을 기다리는 상태입니다.

terminate

  • 실행을 마친 상태입니다.

프로세스 상태

Created

  • 작업(Job)을 커널에 등록한 상태입니다. PCB를 할당받으며 프로세스가 생성됩니다.

Running

  • 메모리와 프로세서(CPU)를 할당받은 상태입니다.
  • 실제로 작업을 실행하는 상태입니다.
  • 프로세서를 뺐겨 Ready상태가 되는 걸 Preemption이라고 합니다.
  • 추가적인 자원이 필요해(I/O를 하기 위해) blocked(asleep)상태가 되는 걸 Block(sleep)이라고 합니다.

Ready

  • 프로세서 이외의 모든 자원을 할당받은 상태입니다.
  • CPU만 할당받으면 즉시 실행될 수 있습니다.

Blocked(asleep)

  • I/O를 기다리는 상태입니다.
  • 메모리는 할당받았지만 프로세서와 기타자원이 없는 상태입니다.

Suspended

  • 메모리를 할당받지 못한 상태입니다.
  • memory image를 swap device에 저장하고 있습니다.
  • Suspended상태에서 메모리를 받으면 Swap-in, 원래 상태에서 메모리를 뺐기면 Swap-out이 됩니다.
  • Suspended Ready와 Suspended Block상태가 존재합니다.

기타

extends thread와 implements Runnable의 차이

  • 별 차이 없습니다. 단지 extends와 implements의 차이(단일상속, 다중상속)일 뿐입니다.

CompletableFuture

  • CompletableFutere은 람다기반 비동기식 병렬 프로그래밍 기법입니다.
  • Future와 CompletionStage를 구현한 클래스입니다.
  • 간단하게 비동기 처리의 결과를 가져올 수 있게 설계된 클래스 입니다.

데몬 스레드

  • 자동으로 호출 및 종료됩니다.(사용자가 직접적으로 제어하지 않습니다)
  • 사용자 스레드를 돕는 역할을 합니다.

프로세스 종료시점

  • 사용자 스레드가 하나도 존재하지 않을 때 종료됩니다.
  • 메인 스레드의 종료시점과는 무관합니다.

스레드 그룹

  • 서로 관련된 스레드를 그룹으로 묶어서 관리하는 걸 의미합니다.
  • 모든 스레드는 반드시 하나의 스레드 그룹에 포함되어야 합니다.
  • 그룹을 지정하지 않고 생성한 스레드는 main스레드 그룹에 포함됩니다.
  • 생성된 스레드는 자신을 생성한 스레드(부모 스레드)의 그룹과 우선순위를 상속받습니다.

References

profile
기록하고 정리하는 걸 좋아하는 개발자.

0개의 댓글