본문 바로가기
💻 개발/Java

동시성 프로그래밍 - 기초

by 컴쏘 2024. 12. 10.

 

Java에서 마주할 수 있는 동시성에 대해서 알아보자. 

 

| 동시성 vs 병렬성 

동시성과 병렬성은 자주 비교된다. 

  • 동시성(Concurrency) : 하나의 CPU가 여러 작업을 빠르게 번갈아 수행함으로써, 사람이 보기에는 여러 작업이 동시에 진행되는 것처럼 보이는 상태이다. 
    • 1개의 CPU에서 작업을 나눠 실행하고, 스케줄러가 각 작업의 실행시간을 조율한다. 
    • 한 번에 한 작업만 실행되지만, 작업 간의 전환이 매우 빠르게 이루어진다. 
  • 병렬성(Parallelism) : 여러 CPU각각 독립적으로 여러 작업을 동시에 처리하는 상태이다. 
    • CPU N개가 N개의 작업을 동시에 처리한다.
    • 멀티 코어 프로세서에서 여러 스레드가 동시에 실행된다. 

 

지난번에, ArrayList와 Vector를 비교할 때, Thread-Safe에 대해 언급을 한 적이 있다. 

 

Thread Safe에 대해 좀 더 알아보자. 

| Thread Safe 

Thread Safe멀티 스레드 환경에서 어떤 함수나 변수, 혹은 객체가 여러 스레드로부터 동시에 접근이 이루어져도 프로그램 실행에 문제가 없음을 뜻한다. 

  • 데이터 손실, 충돌, 또는 비정상적인 동작 없이 안정적으로 작동한다. 

 

| 가시성 vs 원자성 

가시성 문제와 원자성 문제는 여러 개의 스레드가 사용될 때 언급될 수 있는 문제들이다. 

  • 가시성 문제여러 개의 스레드가 사용됨에 따라, CPU 캐시 메모리와 RAM의 데이터가 서로 일치하지 않아 생기는 문제이다. 
    • CPU 캐시와 메인 메모리 간 데이터 불일치로 발생한다.
    • 스레드 A가 값을 변경했지만, 스레드 B는 캐시된 값을 읽기 때문에 최신 값을 보지 못한다. 
  • 원자성 문제여러 개의 스레드공유 자원에 동시에 쓰기 연산을 할 경우 잘못된 결과를 반환하는 것을 의미한다. 
    • count++ 연산과 같이 두 스레드가 동시에 실행했을 때 값이 덮어써지는 예시를 많이 들기도 한다. 

 

그렇다면, 동시성 문제는 어떻게 해결할 수 있을까? 

  1. volatile 키워드 사용 (가시성 문제) 
  2. synchronized (원자성 문제) 
  3. atomic (원자성 문제) 

| volatile

volatileCPU 캐시 메모리를 거치지 않고, RAM으로 직접 데이터를 읽고 쓰는 작업을 수행함으로써 가시성 문제를 해결할 수 있는 키워드이다. 

  • CPU 캐시를 우회하고, 항상 메인 메모리에서 값을 읽고 쓰도록 강제하는 것이다. 
  • 따라서, 스레드가 항상 최신 값을 읽고 쓸 수 있도록 보장한다. 
  • 하지만, 원자성을 보장하지 않아, 복잡한 연산(ex. count++)에는 적합하지 않다. 

 

| synchronized

synchronized lock을 통해 동기화를 수행하여 원자성 문제를 해결하는 키워드 이다. 메소드에서 사용하거나 block 단위로 사용할 수 있다. 

  • 임계 구역(Critical Section) 설정하여 한 번에 하나의 스레드만 접근 가능하는 것이다. (한 번에 하나의 스레드만 임계 구역에 접근할 수 있도록 하여 데이터 일관성을 유지한다.)
  • Java에서 synchronized는 모니터를 사용하여 구현되기 때문에, 모든 객체는 Monitor lock을 가지며 synchronized는 해당 객체의 락을 획득하여, 다른 스레드가 접근하지 못하도록 한다. (synchronized는 해당 객체의 모니터를 잠그는 역할)
  • Blocking 방식이므로, 락을 기다리는 동안 스레드가 작업을 멈춘다. 
  • 따라서, 한 스레드가 lock을 얻어서 임계 영역에 접근하면 해당 영역에 접근하고 싶은 모든 스레드가 blocking이 걸려 아무 일도 못하며 스레드 상태를 변경하는 비용때문에 성능이 좋지 않다. 

 

| atomic

java.util.concurrent.atomic 패키지로, *CAS(Compare And Swap) 알고리즘을 사용해 non-blocking으로 원자성 문제를 해결한다. 

  • 작업이 다른 스레드의 개입 없이 한 번에 이루어지는 최소 단위atomic이라고 한다. 
  • atomic 연산자는 해당 작업이 동시성 문제 없이 안전하게 실행된다는 특징이 있다.
  • 대표적으로 변수 할당이 있다.
  • atomic 타입멀티 스레드 환경에서 원자성을 보장하기 위한 개념이다. 

 

*CAS : 병렬 프로그래밍에서 동시성 문제를 해결하기 위해 사용하는 비차단(non-blocking) 알고리즘. 공유 자원을 안전하게 업데이트하기 위해, 현재 값과 예상 값(expected value)을 비교한 후, 예상 값이 일치하면 새로운 값으로 교체(swap)한다. 

'💻 개발 > Java' 카테고리의 다른 글

동시성 프로그래밍 - 심화  (0) 2024.12.19
JCF와 스레드 - JCF 심화 및 스레드  (0) 2024.12.05
JCF와 스레드 - JCF 기초  (0) 2024.12.04
Java 모의 면접 후기  (0) 2024.12.01
11/25 - TIL : JPA와 N+1  (0) 2024.11.25