운영체제 - 프로세스와 스레드, 컨텍스트 스위칭, 동기화, 데드락, 스레드풀 정리

이진경·2025년 5월 19일
0

CS

목록 보기
1/1

1️⃣ 프로세스와 스레드의 차이는?

항목 프로세스 (Process) 스레드 (Thread)
정의 실행 중인 프로그램 프로세스 내 작업 단위
메모리 독립된 메모리 공간 사용 프로세스의 메모리 공유
생성 비용 상대적으로 큼 (자원 별도 필요) 가벼움 (자원 공유)
안정성 한 프로세스 종료돼도 다른 프로세스에 영향 없음 한 스레드 오류 시 전체 프로세스 영향 가능성 있음
예시 크롬, VSCode, Discord 등 실행 프로그램 크롬에서 여러 탭을 동시에 여는 구조

💡 스레드는 프로세스의 작업 단위로, 여러 스레드가 하나의 프로세스 내에서 CPU를 공유하면서 동시에 실행될 수 있습니다.

2️⃣ 컨텍스트 스위칭이란?

컨텍스트 스위칭(Context Switching)은 운영체제가 CPU를 A 스레드에서 B 스레드로 전환할 때 발생하는 상태 저장/복원 작업입니다.
스레드마다 레지스터, 프로그램 카운터 등의 상태(컨텍스트)를 가짐
A 스레드의 상태를 저장하고 B 스레드의 상태를 불러오는 과정
이 과정에서 CPU 리소스를 소비하므로 너무 잦으면 오버헤드 발생

💡 멀티태스킹이 가능하게 해주는 기술이지만, 너무 잦으면 성능 저하의 원인이 됩니다.

3️⃣ 멀티스레드 환경에서 동기화는 왜 필요한가?

멀티스레드 환경에서는 여러 스레드가 공유 자원(예: 전역 변수, 파일, DB)에 동시에 접근할 수 있기 때문에,
데이터 충돌이나 상태 불일치(race condition)가 발생할 수 있습니다.

class ThreadEx21 {
	public static void main(String args[]) {
		Runnable r = new RunnableEx21();
		new Thread(r).start();
		new Thread(r).start();
	}
}

class Account {
	private int balance = 1000;

	public  int getBalance() {
		return balance;
	}

	public void calc(int data){
		if(balance >= data) {
			try { Thread.sleep(1000);} catch(InterruptedException e) {}
			balance -= data;
		}
	}
}

class RunnableEx21 implements Runnable {
	Account acc = new Account();

	public void run() {
		while(acc.getBalance() > 0) {
			int randomData = (int)(Math.random() * 3 + 1) * 100;
			acc.calc(randomData);
			System.out.println("balance:"+acc.getBalance());
		}
	} // run()
}

실행 결과:
balance:800
balance:700
balance:500
balance:500
balance:200
balance:0
balance:-200

위 코드를 실행하면 balance값이 음수가 된다.
한 쓰레드가 calc()의 if문에서 조건식을 만족해서 data를 뺀순간, 다른 스레드에게 제어권이 넘어가서 다른 쓰레드가 또 if문의 조건식을 만족해서 data를 빼버리는 순간이 온다.
그래서 결국 balance값은 음수가 되서 프로그램이 종료된다.
-> calc() 함수에 synchronized키워드를 붙여서 한 번에 하나의 스레드만 임계 영역을 실행할 수 있게 만듭니다.

임계 영역(Critical Section)은 둘 이상의 스레드가 동시에 접근할 경우 문제를 일으킬 수 있는 코드 구간입니다.

...
public synchronized void calc(int data){
	if(balance >= data) {
		try { Thread.sleep(1000);} catch(InterruptedException e) {}
		balance -= data;
	}
}
...

실행결과
balance:900
balance:700
balance:400
balance:200
balance:100
balance:100
balance:100
balance:100
balance:100
balance:100
balance:100
balance:0
balance:0

이렇게 해줌으로써, calc()가 호출되면 calc()가 종료되어 lock이 반납될때까지 다른 쓰레드는 calc()를 호출하더라도 대기 상태에 머무르게 된다.

💡 동기화는 여러 스레드가 동시에 공유 자원에 접근할 때 데이터 정합성을 보장하기 위해 필요합니다.

4️⃣ 데드락(Deadlock)이란? 방지 방법은?

데드락(Deadlock)은 두 개 이상의 스레드가 서로가 가진 자원을 기다리며 무한 대기 상태에 빠지는 현상입니다.

예시:

class Shared {
    synchronized void methodA(Shared other) {
        System.out.println(Thread.currentThread().getName() + " calls methodA");
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        other.methodB(this);  // 여기서 데드락 가능
    }

    synchronized void methodB(Shared other) {
        System.out.println(Thread.currentThread().getName() + " calls methodB");
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        other.methodA(this);  // 여기서 데드락 가능
    }
}

public class DeadlockExample {
    public static void main(String[] args) {
        Shared s1 = new Shared();
        Shared s2 = new Shared();

        new Thread(() -> s1.methodA(s2), "Thread-1").start();
        new Thread(() -> s2.methodB(s1), "Thread-2").start();
    }
}

→ 서로가 서로를 기다리며 무한 대기 → 데드락 발생

✅ 데드락 발생 4가지 조건 (전부 충족 시 발생)

상호 배제(Mutual Exclusion)
– 자원은 한 번에 하나의 스레드만 사용 가능
점유 대기(Hold and Wait)
– 자원을 점유한 상태에서 다른 자원을 기다림
비선점(No Preemption)
– 자원을 강제로 뺏을 수 없음
환형 대기(Circular Wait)
– 자원을 서로 물고 물고 있는 순환 대기 상태

방지 방법
1. 자원 획득 순서를 일관되게 정함

class Shared {
    void methodA(Shared other) {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " acquired lock on this");
            try { Thread.sleep(100); } catch (InterruptedException e) {}

            synchronized (other) {
                System.out.println(Thread.currentThread().getName() + " acquired lock on other");
            }
        }
    }

    void methodB(Shared other) {
        // methodA와 동일한 순서로 락 획득
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " acquired lock on this");
            try { Thread.sleep(100); } catch (InterruptedException e) {}

            synchronized (other) {
                System.out.println(Thread.currentThread().getName() + " acquired lock on other");
            }
        }
    }
}
  1. 타임아웃 설정
Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();

if (lock1.tryLock(1, TimeUnit.SECONDS)) {
    try {
        if (lock2.tryLock(1, TimeUnit.SECONDS)) {
            try {
                // 작업
            } finally {
                lock2.unlock();
            }
        }
    } finally {
        lock1.unlock();
    }
}

💡 데드락은 리소스를 차례로 요청하다가 서로 물고 물려서 아무 것도 못하는 상태로, 이를 방지하려면 자원 접근 순서나 타임아웃 설정이 중요합니다.

📚 참고 링크

profile
기록남기기

0개의 댓글