Skip to content

2주차 월요일 그룹 6

Semin Choi edited this page Jul 1, 2024 · 4 revisions

Process & Thread

Process Thread
설명 컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램 프로세스 내에서 실행되는 여러 흐름의 단위
멀티 - 컨텍스트 스위칭 비용이 높다
- 데이터를 여러 프로세스간 공유하기 어렵다
- 공유 자원을 사용해서 Context-switching을 하기 때문에 적은 오버헤드를 가진다.
- 스택 이외의 코드, 데이터, 힙 영역을 공유하기 때문에, 데이터 공유에는 용이하지만 주의가 필요하다.
사용 - fork : 자식 프로세스 생성 명령어

데드락 발생 조건

  1. 상호 배제(Mutual Exclusion)

    : 자원은 한 번에 한 프로세스만이 사용할 수 있어야 한다.

  2. 점유 대기 (Hold and Wait)

    : 최소한 하나의 자원을 점유하고 있으면서 다른 프로세스에 할당되어 사용되고 있는 자원을 추가로 점유하기 위해 대기하는 프로세스가 있어야 한다.

    즉, 이미 자원을 사용중인데, 다른 프로세스가 사용중인 자원을 사용하기 위해 대기하고 있는 상태의 프로세스가 존재해야 한다.

  3. 비선점 (No preemption)

    : 다른 프로세스에 할당된 자원은 사용이 끝날 때까지 강제로 빼앗을 수 없어야 한다.

  4. 순환 대기 (Circular wait)

    : 프로세스의 집합에서 P0은 P1이 점유한 자원을 대기하고 P1은 P2가 점유한 자원을 대기하고, P2...Pn-1d은 Pn이 점유한 자원을 대기하며 Pn은 P0이 점유한 자원을 요구해야 한다.

Thread Pool

  • 일정 개수의 Thread를 미리 만들어 관리하는것.
  • 사용 이유 :
  1. Thread를 동시에 수많은 개수를 생성하게 된다면 메모리가 부족해진다. 따라서 일정 개수의 Thread를 만든 뒤, 재사용을 하는 방법으로 Thread를 관리하게되면 메모리를 아낄 수 있다.
  2. 또한 Thread를 생성하는 비용이 크기 때문에, 이를 아낄 수 있다는 장점이 있다.

Thread Pool Executor

  • Thread Pool 을 효율적으로 관리하기 위한 라이브러리
  • ThreadPoolExecutor에는 3개의 키워드가 있다.
    • Core-Pool-Size : 동시에 실행될 수 있는 Thread의 기본 사이즈
    • Maximum-Pool-Size : 최대로 실행될 수 있는 Thread의 최대 사이즈
    • Working-Queue-Size : 실행되지 못하고 기다릴 수 있는 Queue의 사이즈
  • Thread Pool Executor는 다음과 같이 실행된다.
    1. core-pool-size가 3, maximum-pool-size 가 10, working-queue-size 가 5 라고 하자.
    2. 동시에 10개의 Thread를 실행시켜달라는 요청이 들어왔다고 가정하자.
    3. 먼저, core-pool-size의 개수만큼 thread가 실행된다. (남은 쓰레드 7개)
    4. 그런 뒤, 남은 쓰레드를 working queue에 넣는다. (남은 쓰레드 2개)
    5. 남은 쓰레드를 최대 maximum-pool-size 만큼 실행시킨다. (남은 쓰레드 0개)

Volatile 명령어

volatile 키워드가 붙은 변수는 항상 Main Memory에서 데이터를 Read&Write 합니다.

  • volatile 키워드를 사용하지 않는 변수는 성능향상을 위해 메인 메모리에서 읽은 변수 값을 CPU cache에 저장합니다.
  • 멀티 스레드 환경에서는 다른 스레드에서 공유 데이터를 변경했을 때 CPU cache간의 불일치가 발생할 수 있는데 volatile키워드를 사용하여 이를 방지할 수 있습니다.

하지만, volatile키워드를 사용해도 여러 스레드에서 동시에 데이터에 접근할 수 있기 때문에 다음과 같은 문제가 발생할 수 있습니다.

public class Example {
  public volatile int count = 0;
  
  public void increaseCount() {
    count++;
  }
}
  1. A 스레드에서 increaseCount() 호출하여 count 변수를 메인 메모리에서 Read (count = 0)
  2. B 스레드에서 increaseCount() 호출하여 count 변수를 메인 메모리에서 Read (count = 0)
  3. A 스레드에서 count 변수를 증가시키고 메모리에 Write
  4. B 스레드에서 count 변수를 증가시키고 메모리에 Write

위와 같은 차례로 Task를 수행하게 되면 두 스레드 모두 같은 값을 가져갔기 때문에 increaseCount() 가 두번 수행되어도 count = 1 인 상태로 마무리 됩니다.

참고 : https://nesoy.github.io/articles/2018-06/Java-volatile

synchronized 키워드

자바에서 synchronized 를 사용하면 여러개의 스레드가 한 개의 자원을 접근하려고 할 때 나머지 스레드들은 데이터에 접근할 수 없도록 막습니다.

  • Method에 사용하는 경우의 대상
    • 인스턴스 단위 동기화 → 인스턴스 메소드, 인스턴스 메소드 코드 블록
    • 클래스 단위 동기화 → 스태틱 메소드, 스태틱 메소드 코드 블록

주의할 점

  • 성능이 느려질 수 있으며 데드락이 발생할 수 있습니다.

자바 Thread와 OS Thread

자바의 Thread 객체 하나는 OS의 Thread 하나와 매칭된다.

ThreadLocal이란?

ThreadLocal을 사용하면 Thread 별로 다른 값을 가지는 변수를 생성할 수 있다.

Spring에서 SecurityContextHolder 등은 요청마다 다른 정보를 저장해야 하므로 ThreadLocal을 사용하여 스레드 별로 저장한다. (1 요청 = 1 스레드)

자바 Thread를 사용하는 법

  1. Runnable interface를 구현하는 방법

    class MyThread implements Runnable{
    	@Override
    	public void run(){
    		//동작 구현
    	}
    }
    
    /** 실행 방법 **/
    Thread thread = new Thread(new MyThread());
    thread.start();
  2. Thread 클래스를 상속받아 구현하는 방법

    class MyThread extends Thread{
    	@Override
    	public void run(){
    		//동작 구현
    	}
    }
    
    /** 실행 방법 **/
    Thread thread = new MyThread();
    thread.start();

왜 run() 메서드를 구현해놓고, Thread.start() 로 thread를 실행시키는지?

  • run() 메서드는 콜스택을 생성하지 않음.
  • 메인의 콜스택에 함수에 대한 스택을 쌓은 뒤 실행하게됨. 이는 단일 쓰레드(메인 쓰레드)에서 실행하는것임.
  • start() 메서드는 jvm이 실행시에 별도의 콜스택을 생성하게 됨.
  • jvm이 콜스택을 돌며 동시에 처리되는것처럼 보이도록 실행하게됨.

자바 Thread에서 동시성 관리하는 방법

  1. synchronized keyword를 통한 암시적 Lock

    class Count {
        private int count;
        public synchronized int view() {return count++;}
    }
  2. ReentrantLock을 통한 명시적 Lock

    public class CountingTest {
        public static void main(String[] args) {
            Count count = new Count();
            for (int i = 0; i < 100; i++) {
                new Thread(){
                    public void run(){
                        for (int j = 0; j < 1000; j++) {
                            count.getLock().lock();
                            System.out.println(count.view());
                            count.getLock().unlock();
                        }
                    }
                }.start();    
            }  
        }
    }
    
    class Count {
        private int count = 0;
        private Lock lock = new ReentrantLock();
        public int view() {
                return count++;
        }
        public Lock getLock(){
            return lock;
        };
    }
    
    // 재정의한 run을 실행 -> main 스레드 위에 생성 -> 단일 스레드
    // 따라서 run이 아닌 start로 실행하게 됨

Virtual Threads

JDK 21부터 사용 가능한 Virtual Thread. (Virtual Thread 쓰는 Spring은 WebFlux와 성능 차이가 거의 없다능)

https://d2.naver.com/helloworld/1203723

👼 개인 활동을 기록합시다.

개인 활동 페이지

🧑‍🧑‍🧒‍🧒 그룹 활동을 기록합시다.

그룹 활동 페이지

🎤 미니 세미나

미니 세미나

🤔 기술 블로그 활동

기술 블로그 활동

📚 도서를 추천해주세요

추천 도서 목록

🎸 기타

기타 유용한 학습 링크

Clone this wiki locally