[Concurrency] Thread의 기본

이홍준·2023년 10월 16일

Concurrecy 

목록 보기
1/3

대용량 처리를 하는 데 있어서 쓰레드를 공부해야 되는 것은 필수라고 생각된다. 해당 사용되는 라이브러리를 먼저 사용하는 것보다 동작 원리를 먼저 파악해야 된다는 것을 인지하고 기초부터 알아보고자 한다.

기본 개념

  1. Thread: 동일 프로세스 내에서 독립적으로 실행되는 여러 실행 흐름
  2. Java에서 사용되는 Thread 사용 방법
    • Thread
      public class Thread implements Runnable {
      	// ...
      }
    • Runnable
      @FunctionalInterface
      public interface Runnable {
          
          public abstract void run();
      }

Thread life cycle in java

  1. 상태

    • NEW: 쓰레드가 생성되었지만 아직 시작되지 않은 상태
    • RUNNABLE: 쓰레드가 실행 가능한 상태(실행 중 or 실행 준비)
    • BLOCKED: 쓰레드가 기다리는 상태(Lock을 획득하기 위해)
    • WAITING: 쓰레드가 무한히 기다리고 있는 상태(다른 쓰레드가 통지할 때 까지 기다림)
    • TERMINATED(DEAD): 쓰레드가 실행이 종료된 상태
  2. 메서드

    • start(): 쓰레드 시작 (new → runnable)
    • run(): 쓰레드의 주요 작업을 정의한다. start() 메서드에 의해 호출
    • join(): 현재 쓰레드가 지정된 쓰레드의 종료를 기다림. 다른 쓰레드가 종료될 때까지 현재 쓰레드를 일시 중단 → 해당 scope를 가지고 있는 Thread에 종속
    • sleep(ms): 현재 쓰레드를 지정된 시간 동안 일시 중단
  3. 예시 코드

    public class ThreadTest {
        @Test
        public void test(){
            Thread thread = new Thread(() -> {
                System.out.println("쓰레드 실행 중...");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("쓰레드 종료.");
            });
    
            System.out.println("쓰레드 상태 (NEW): " + thread.getState());  // NEW
            thread.start();
            System.out.println("쓰레드 상태 (RUNNABLE): " + thread.getState());  // RUNNABLE
    
            try {
                thread.join();  // 메인 쓰레드가 지정된 스레드의 종료를 기다림
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("쓰레드 상태 (TERMINATED): " + thread.getState());  // TERMINATED
        }
    }
  4. 출력

    
     스레드 상태 (NEW): NEW
     스레드 상태 (RUNNABLE): RUNNABLE
     스레드 실행 중...
     스레드 종료.
     스레드 상태 (TERMINATED): TERMINATED
     

Waiting vs Blocked

여기서 난 Blocked상태와 Waiting상태가 대체로 비슷한 것 같아 차이점을 정리해 보았다.

  • Wating
    • 쓰레드가 특정 작업이나 조건을 만족할 때 까지 대기하고 있는 상태
    • wait(), sleep(), join(), LockSupport.park() 등과 같이 명시적으로 대기시킨 상태
    • 특정 조건(notify(), notifyAll()이 만족되거나 외부 이벤트, 신호에 의해 대기상태를 해제 할 수 있음
  • Blocked
    • 쓰레드가 동기화된 블록 또는 메서드에 접근하려고 시도하려 할때, 이미 다른 쓰레드에 의해 Lock에 걸려있는 상황이면 Blocked 상태로 전환된다.
    • 이 상태는 런타임에 자동으로 발생하며 개발자가 직접 만들어주는 상태가 아니다.

락(Lock)을 획득

Lock을 획득한다는 말이 정확히 무엇인가? 에서 궁금증을 해결하기 위해 정리를 해보았다.

→ 특정 쓰레드가 공유 리소스에 대한 접근 권한을 얻는다는 의미이다. Lock을 획득하는 동안 다른 쓰레드가 공유 리소스를 접근하지 못하고 대기한다.

  • Java 예시 코드
    // 블록 정의
    public void myMethod1(){
    	synchronized (lock) {
    	    // 구현부분
    	}
    }
    
    // 메서드 전체 정의
    public synchronized void myMethod2() {
        // critical section
    }

→ synchronized 블록이 커지면 Lock범위가 넓어짐에 따라 그만큼 대기시간도 길어지기 때문에 필요한 만큼 사용해야만 한다.

Synchronized에 대하여

Java에서 Lock획득에 대해 정의할 때 synchronized 키워드를 사용하게 되는데 기능에 대해서 자세히 정리해보고자 한다.

주요 기능

  • Lock 획득과 해제: 쓰레드가 블록 또는 메서드에 접근하려고 시도하려할 때, Lock을 획득하고 실행이 끝나면 해제
  • Mutex: 데이터의 일관성과 무결성을 보장하기 위해 기본적으로 Mutex를 사용한다.

Mutex vs Semaphore

Synchronized은 Mutex 매커니즘을 사용한다고 알려져있다. 그래서 학부 시절 운영체제 수업에 배웠던 Mutex와 Semaphore의 차이가 궁금했기 때문에 정리해보았다.

  1. Mutex
    • 상호 배제: Mutex는 한 번에 하나의 쓰레드만 공유 자원에 접근할 수 있도록 하는 Lock 매커니즘
    • 소유권: Lock을 획득한 쓰레드만이 Lock을 해제할 수 있다.
  2. Semaphore
    • 리소스 카운터: Semaphore는 여러 개의 쓰레드가 공유 자원에 동시에 접근할 수 있는 경우에 사용됨. Semaphore의 값은 동시에 접근할 수 있는 최대 쓰레드 수
    • 소유권 없음: Mutex와 달리 소유권 개념이 없음 → 한 쓰레드가 세마포어를 획득한 다음 다른 쓰레드가 그것을 해제할 수 있다.

주요 차이점은 Mutex는 단일 접근, Semaphore는 다수 접근이다. 그리고 소유권의 유무의 차이가 있다.

결론

동시성에 대해 공부하기 위해 쓰레드의 기본에 대해 정리해 보았다. 생성과 실행은 Thread, Runnable을 사용하고 생명주기는 NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING,TERMINATE의 상태들이 있다. 그리고 쓰레드의 동기화를 위한 수단으로 synchronized 키워드, 관리하는 기법인 Mutex, Semaphore등에 대해서 알아보았다.


References

profile
I'm not only a web developer.

0개의 댓글