λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ’» 운영체제

[java] Thread μ‹€μŠ΅ (μš΄μ˜μ²΄μ œπŸ¦– κ°•μ˜-Chpater4. Thread & Cocurrency)

by λΆˆνƒ€λŠ” μ°Έμƒˆ 2025. 2. 23.

1. Threadλž€?

μš΄μ˜μ²΄μ œμ—μ„œ ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€(process)λŠ” μ—¬λŸ¬ 개의 μ“°λ ˆλ“œ(thread)λ₯Ό κ°€μ§ˆ 수 μžˆλ‹€. μ“°λ ˆλ“œλŠ” ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ μ‹€ν–‰ 흐름을 μ˜λ―Έν•˜λ©°, ν”„λ‘œμ„ΈμŠ€μ˜ μžμ›μ„ κ³΅μœ ν•˜λ©΄μ„œλ„ 독립적인 μ‹€ν–‰ 흐름을 κ°€μ§„λ‹€. 즉, ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ μ—¬λŸ¬ μž‘μ—…μ„ λ™μ‹œμ— μˆ˜ν–‰ν•  수 μžˆλ„λ‘ 도와쀀닀.

1.1. ν”„λ‘œμ„ΈμŠ€μ™€ μ“°λ ˆλ“œμ˜ 차이

  • ν”„λ‘œμ„ΈμŠ€λŠ” μ‹€ν–‰ 쀑인 ν”„λ‘œκ·Έλž¨μ˜ μΈμŠ€ν„΄μŠ€λ‘œ, 독립적인 λ©”λͺ¨λ¦¬ 곡간(Code, Data, Heap, Stack λ“±)을 κ°€μ§„λ‹€.
  • μ“°λ ˆλ“œλŠ” ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ μ‹€ν–‰λ˜λŠ” μž‘μ€ λ‹¨μœ„λ‘œ, 같은 ν”„λ‘œμ„ΈμŠ€ λ‚΄μ˜ λ‹€λ₯Έ μ“°λ ˆλ“œλ“€κ³Ό Code, Data, Heap, File λ“±μ˜ μžμ›μ„ κ³΅μœ ν•˜μ§€λ§Œ, ν”„λ‘œκ·Έλž¨ μΉ΄μš΄ν„°(PC), λ ˆμ§€μŠ€ν„°, μŠ€νƒ(Stack)은 κ°œλ³„μ μœΌλ‘œ κ΄€λ¦¬λœλ‹€.
  • λ”°λΌμ„œ ν”„λ‘œμ„ΈμŠ€ κ°„ 톡신(IPC: Inter-Process Communication)은 μƒλŒ€μ μœΌλ‘œ λΉ„μš©μ΄ 많이 λ“€μ§€λ§Œ, μ“°λ ˆλ“œ κ°„ 톡신은 곡유 λ©”λͺ¨λ¦¬λ₯Ό ν™œμš©ν•˜μ—¬ 훨씬 효율적으둜 μˆ˜ν–‰λ  수 μžˆλ‹€.

1.2. CPU μŠ€μΌ€μ€„λ§ λ‹¨μœ„

CPUλ₯Ό μ μœ ν•˜λŠ” λ‹¨μœ„λŠ” **ν”„λ‘œμ„ΈμŠ€ ID(pid)κ°€ μ•„λ‹ˆλΌ μ“°λ ˆλ“œ ID(tid)**이닀. 즉, μ‹€μ œλ‘œ CPUλ₯Ό μ μœ ν•˜κ³  μ‹€ν–‰ν•˜λŠ” 것은 ν”„λ‘œμ„ΈμŠ€κ°€ μ•„λ‹ˆλΌ μ“°λ ˆλ“œμ΄λ©°, 각 μ“°λ ˆλ“œλŠ” 독립적인 ν”„λ‘œκ·Έλž¨ μΉ΄μš΄ν„°(PC), λ ˆμ§€μŠ€ν„°, μŠ€νƒμ„ κ°€μ§„λ‹€.

 

2. λ©€ν‹°μ“°λ ˆλ”©(Multithreading)

λ©€ν‹°μ“°λ ˆλ”©μ€ ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ μ—¬λŸ¬ 개의 μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜μ—¬ λ³‘λ ¬λ‘œ μ‹€ν–‰ν•˜λŠ” κΈ°μˆ μ΄λ‹€. 이λ₯Ό 톡해 ν”„λ‘œκ·Έλž¨μ˜ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆλ‹€.

2.1. λ©€ν‹°μ“°λ ˆλ”©μ˜ μž₯점

  1. 응닡성(Responsiveness):
    • μ‚¬μš©μžμ˜ μž…λ ₯을 λΉ λ₯΄κ²Œ μ²˜λ¦¬ν•  수 있음
    • 예λ₯Ό λ“€μ–΄, GUI ν”„λ‘œκ·Έλž¨μ—μ„œ UI μ“°λ ˆλ“œκ°€ λ³„λ„λ‘œ λ™μž‘ν•˜λ©΄, μ‹œκ°„μ΄ 였래 κ±Έλ¦¬λŠ” μ—°μ‚° 쀑에도 UIκ°€ λ©ˆμΆ”μ§€ μ•Šκ³  λ°˜μ‘ν•  수 있음
  2. μžμ› 곡유(Resource Sharing):
    • ν”„λ‘œμ„ΈμŠ€ λ‚΄μ—μ„œ Code, Data, Heap 등을 κ³΅μœ ν•˜μ—¬ μ“°λ ˆλ“œ κ°„ 데이터 κ΅ν™˜μ΄ μš©μ΄ν•¨
    • λ³„λ„μ˜ 곡유 λ©”λͺ¨λ¦¬λ₯Ό λ§Œλ“€ ν•„μš” 없이 κ°„λ‹¨ν•˜κ²Œ 데이터λ₯Ό 주고받을 수 있음
  3. κ²½μ œμ„±(Economy):
    • μƒˆλ‘œμš΄ ν”„λ‘œμ„ΈμŠ€λ₯Ό μƒμ„±ν•˜λŠ” 것보닀 μƒˆλ‘œμš΄ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜λŠ” 것이 λΉ„μš©μ΄ 적게 듦
    • μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­(Context Switching)도 ν”„λ‘œμ„ΈμŠ€ 간보닀 μ“°λ ˆλ“œ κ°„μ—μ„œ 더 빠름
  4. 병렬성(Scalability):
    • λ©€ν‹°μ½”μ–΄ μ‹œμŠ€ν…œμ—μ„œ μ“°λ ˆλ“œλ₯Ό ν™œμš©ν•˜λ©΄ μ—¬λŸ¬ μž‘μ—…μ„ λ™μ‹œμ— μ‹€ν–‰ν•˜μ—¬ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 있음
    • CPU μ½”μ–΄κ°€ λ§Žμ„μˆ˜λ‘ 병렬 μ‹€ν–‰ νš¨κ³Όκ°€ λ”μš± κ·ΉλŒ€ν™”λ¨

2.2. 예제: ν΄λΌμ΄μ–ΈνŠΈ-μ„œλ²„ λͺ¨λΈμ—μ„œμ˜ λ©€ν‹°μ“°λ ˆλ”©

예λ₯Ό λ“€μ–΄, ν΄λΌμ΄μ–ΈνŠΈ-μ„œλ²„ μ‹œμŠ€ν…œμ—μ„œ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„œλ²„μ— μš”μ²­μ„ 보낼 λ•Œ, μ„œλ²„λŠ” 각 μš”μ²­μ„ κ°œλ³„ μ“°λ ˆλ“œλ‘œ μ²˜λ¦¬ν•  수 μžˆλ‹€. 이λ₯Ό 톡해 ν•˜λ‚˜μ˜ μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” λ™μ•ˆμ—λ„ λ‹€λ₯Έ μš”μ²­μ„ 계속 받을 수 μžˆμœΌλ―€λ‘œ μ„œλ²„μ˜ 응닡성이 ν–₯μƒλœλ‹€.

3. Javaμ—μ„œμ˜ μ“°λ ˆλ“œ 생성 방법: (1)Thread 클래슀 상속

JavaλŠ” λ©€ν‹°μ“°λ ˆλ”©μ„ 기본적으둜 μ§€μ›ν•˜λ©°, λ‹€μŒκ³Ό 같은 λ°©μ‹μœΌλ‘œ μ“°λ ˆλ“œλ₯Ό λ§Œλ“€ 수 μžˆλ‹€.

λ‹€μŒμ€ Thread 클래슀λ₯Ό μƒμ†ν•˜μ—¬ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜κ³  μ‹€ν–‰ν•˜λŠ” μ˜ˆμ œμ΄λ‹€.

3.1. μ½”λ“œ μ„€λͺ…

package ch04;

class MyThread1 extends Thread {
	public void run() {
		try {
			while (true) {
				System.out.println("Hello, Thread!");
				Thread.sleep(500);
			}
		}
		catch (InterruptedException ie) {
			System.out.println("I'm interrupted");
		}
	}
}

public class ThreadExample1 {
	
	public static final void main(String[] args) {
		MyThread1 thread = new MyThread1();
		thread.start();
		System.out.println("Hello, My Child");
	}
}

3.2. μ½”λ“œ 뢄석

1) MyThread1 클래슀: μ“°λ ˆλ“œ μ •μ˜

class MyThread1 extends Thread {

 

  • Thread 클래슀λ₯Ό 상속받아 μƒˆλ‘œμš΄ μ“°λ ˆλ“œλ₯Ό μ •μ˜ν•œλ‹€.
  • Thread 클래슀λ₯Ό μƒμ†ν•˜λ©΄ run() λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜μ—¬ μ“°λ ˆλ“œμ˜ λ™μž‘μ„ κ΅¬ν˜„ν•  수 μžˆλ‹€.
public void run() {
    try {
        while (true) {
            System.out.println("Hello, Thread!");
            Thread.sleep(500);
        }
    }
    catch (InterruptedException ie) {
        System.out.println("I'm interrupted");
    }
}

 

  • run() λ©”μ„œλ“œλŠ” μ“°λ ˆλ“œκ°€ 싀행될 λ•Œ μˆ˜ν–‰ν•  λ™μž‘μ„ μ •μ˜ν•œλ‹€.
  • while (true)λ₯Ό μ‚¬μš©ν•˜μ—¬ λ¬΄ν•œ 루프λ₯Ό λŒλ©΄μ„œ "Hello, Thread!"λ₯Ό 0.5μ΄ˆλ§ˆλ‹€ 좜λ ₯ν•œλ‹€.
  • Thread.sleep(500);을 ν˜ΈμΆœν•˜λ©΄ μ“°λ ˆλ“œκ°€ 500ms λ™μ•ˆ λŒ€κΈ°ν•˜κ²Œ λœλ‹€. 이걸 μ—†μ• λ©΄ λ„ˆλ¬΄ λΉ λ₯΄κ²Œ 좜λ ₯λœλ‹€.
  • InterruptedException은 μ“°λ ˆλ“œκ°€ κ°•μ œ μ’…λ£Œλ  λ•Œ λ°œμƒν•˜λŠ” μ˜ˆμ™Έλ‘œ, catch λΈ”λ‘μ—μ„œ "I'm interrupted"을 좜λ ₯ν•˜κ³  μ’…λ£Œλœλ‹€.

2) ThreadExample1 클래슀: μ“°λ ˆλ“œ μ‹€ν–‰

public class ThreadExample1 {
    public static final void main(String[] args) {

 

  • main() λ©”μ„œλ“œλŠ” Java ν”„λ‘œκ·Έλž¨μ˜ μ‹œμž‘μ μ΄λ‹€.
MyThread1 thread = new MyThread1();

 

  • MyThread1 클래슀의 객체(μ“°λ ˆλ“œ)λ₯Ό μƒμ„±ν•œλ‹€.
thread.start();

 

  • start() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄ μƒˆλ‘œμš΄ μ“°λ ˆλ“œκ°€ μƒμ„±λ˜κ³  run() λ©”μ„œλ“œκ°€ μ‹€ν–‰λœλ‹€.
  • run() λ©”μ„œλ“œλ₯Ό 직접 ν˜ΈμΆœν•˜μ§€ μ•Šκ³  start()λ₯Ό ν˜ΈμΆœν•΄μ•Ό λ©€ν‹°μ“°λ ˆλ”©μ΄ λ™μž‘ν•œλ‹€.
System.out.println("Hello, My Child");

 

  • 메인 μ“°λ ˆλ“œμ—μ„œ "Hello, My Child"λ₯Ό 좜λ ₯ν•œλ‹€.
  • 메인 μ“°λ ˆλ“œμ™€ MyThread1 μ“°λ ˆλ“œλŠ” λ™μ‹œμ— μ‹€ν–‰λ˜λ―€λ‘œ 좜λ ₯ μˆœμ„œλŠ” 보μž₯λ˜μ§€ μ•ŠλŠ”λ‹€.

3.3. ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ κ²°κ³Ό

이 ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰ν•˜λ©΄ λ‹€μŒκ³Ό 같은 κ²°κ³Όκ°€ λ‚˜μ˜¨λ‹€.

Hello, My Child
Hello, Thread!
Hello, Thread!
Hello, Thread!
Hello, Thread!
...

 

  • "Hello, My Child"λŠ” 메인 μ“°λ ˆλ“œμ—μ„œ μ‹€ν–‰λœλ‹€.
  • "Hello, Thread!"λŠ” MyThread1 μ“°λ ˆλ“œμ—μ„œ 0.5μ΄ˆλ§ˆλ‹€ 반볡 좜λ ₯λœλ‹€.
  • 메인 μ“°λ ˆλ“œμ™€ MyThread1 μ“°λ ˆλ“œλŠ” λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰λ˜λ―€λ‘œ 좜λ ₯ μˆœμ„œλŠ” 변동될 수 μžˆλ‹€.

3.4. μ“°λ ˆλ“œμ˜ μ‹€ν–‰ μˆœμ„œκ°€ μΌμ •ν•˜μ§€ μ•Šμ€ 이유

  • start()λ₯Ό ν˜ΈμΆœν•˜λ©΄ μš΄μ˜μ²΄μ œκ°€ CPUλ₯Ό ν• λ‹Ήν•  λ•ŒκΉŒμ§€ μ“°λ ˆλ“œλŠ” μ‹€ν–‰λ˜μ§€ μ•ŠλŠ”λ‹€.
  • λ”°λΌμ„œ "Hello, My Child"κ°€ "Hello, Thread!"보닀 λ¨Όμ € 좜λ ₯될 μˆ˜λ„ 있고, 후에 좜λ ₯될 μˆ˜λ„ μžˆλ‹€.
  • μš΄μ˜μ²΄μ œκ°€ μ“°λ ˆλ“œμ˜ μ‹€ν–‰ μˆœμ„œλ₯Ό κ²°μ •ν•˜λ―€λ‘œ μ‹€ν–‰ν•  λ•Œλ§ˆλ‹€ κ²°κ³Όκ°€ λ‹€λ₯Ό 수 μžˆλ‹€.

3.5. 정리

βœ… Thread 클래슀λ₯Ό μƒμ†ν•˜μ—¬ μƒˆλ‘œμš΄ μ“°λ ˆλ“œλ₯Ό λ§Œλ“€μ—ˆλ‹€. βœ… run() λ©”μ„œλ“œμ—μ„œ 반볡적으둜 "Hello, Thread!"λ₯Ό 좜λ ₯ν–ˆλ‹€. βœ… thread.start();λ₯Ό ν˜ΈμΆœν•˜λ©΄ μƒˆλ‘œμš΄ μ“°λ ˆλ“œκ°€ μ‹€ν–‰λœλ‹€. βœ… 메인 μ“°λ ˆλ“œμ™€ μƒˆλ‘œμš΄ μ“°λ ˆλ“œλŠ” λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰λ˜λ―€λ‘œ μ‹€ν–‰ κ²°κ³Όκ°€ μΌμ •ν•˜μ§€ μ•Šλ‹€.

4. Javaμ—μ„œμ˜ μ“°λ ˆλ“œ 생성 방법: (2)Runnable  μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„

Javaμ—μ„œλŠ” Thread 클래슀λ₯Ό μƒμ†ν•˜λŠ” 것 외에도 Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜μ—¬ μ“°λ ˆλ“œλ₯Ό 생성할 수 μžˆλ‹€. 이 방법은 닀쀑 상속이 λΆˆκ°€λŠ₯ν•œ Java의 νŠΉμ„±μƒ λ”μš± μœ μ—°ν•˜κ²Œ μ“°λ ˆλ“œλ₯Ό ν™œμš©ν•  수 μžˆλ„λ‘ ν•΄μ€€λ‹€.

4.1. μ½”λ“œ μ„€λͺ…

package ch04;

class MyThread2 implements Runnable {
    public void run() {
        try {
            while (true) {
                System.out.println("Hello, Runnable!");
                Thread.sleep(500);
            }
        }
        catch (InterruptedException ie) {
            System.out.println("I'm interrupted");
        }
    }
}

public class ThreadExample2 {
    public static final void main(String[] args) {
        Thread thread = new Thread(new MyThread2());
        thread.start();
        System.out.println("Hello, My Runnable Child!");
    }
}

4.1. μ½”λ“œ 뢄석

1) MyThread2 클래슀 μ •μ˜

class MyThread2 implements Runnable {

 

  • MyThread2 ν΄λž˜μŠ€λŠ” Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„(implements).
  • Runnable은 "μ“°λ ˆλ“œκ°€ μ‹€ν–‰ν•  μž‘μ—…μ„ μ •μ˜ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€"
  • 즉, MyThread2λŠ” "μ“°λ ˆλ“œκ°€ μ‹€ν–‰ν•  μž‘μ—…"을 μ •μ˜ν•œ 클래슀

2)  run() λ©”μ„œλ“œ μ •μ˜

public void run() {
    try {
        while (true) {
            System.out.println("Hello, Runnable!");
            Thread.sleep(500);
        }
    }
    catch (InterruptedException ie) {
        System.out.println("I'm interrupted");
    }
}

 

  • run() λ©”μ„œλ“œλŠ” μ“°λ ˆλ“œκ°€ μ‹€ν–‰ν•  μž‘μ—…μ„ μ •μ˜ν•˜λŠ” κ³³
  • while (true) { ... } → λ¬΄ν•œ 루프λ₯Ό λŒλ©΄μ„œ "Hello, Runnable!"을 계속 좜λ ₯
  • Thread.sleep(500); → 0.5초(500ms) λ™μ•ˆ 싀행을 μž μ‹œ 멈좀
  • catch (InterruptedException ie) { ... } → λ§Œμ•½ κ°•μ œλ‘œ μ“°λ ˆλ“œκ°€ μ€‘λ‹¨λ˜λ©΄ "I'm interrupted"λΌλŠ” λ©”μ‹œμ§€λ₯Ό 좜λ ₯

3)  ThreadExample2 ν΄λž˜μŠ€μ—μ„œ μ“°λ ˆλ“œ μ‹€ν–‰

public class ThreadExample2 {
    public static final void main(String[] args) {

 

  • main() λ©”μ„œλ“œλŠ” ν”„λ‘œκ·Έλž¨μ΄ 싀행될 λ•Œ κ°€μž₯ λ¨Όμ € μ‹€ν–‰λ˜λŠ” κ³³

4)  μ“°λ ˆλ“œ 객체 생성

Thread thread = new Thread(new MyThread2());
 
  • new MyThread2() → μš°λ¦¬κ°€ λ§Œλ“  MyThread2 객체λ₯Ό 생성
  • new Thread(new MyThread2()) → Thread 클래슀의 μƒμ„±μžμ— MyThread2 객체λ₯Ό λ„˜κ²¨μ€Œ
  • 이 κ³Όμ •μ—μ„œ Thread 객체가 MyThread2의 run() λ©”μ„œλ“œλ₯Ό μ‹€ν–‰ν•  μ€€λΉ„λ₯Ό 함

5)  μ“°λ ˆλ“œ μ‹œμž‘

thread.start();

 

  • start()λ₯Ό ν˜ΈμΆœν•˜λ©΄ μ“°λ ˆλ“œκ°€ 싀행됨
  • start()λ₯Ό μ‹€ν–‰ν•˜λ©΄ Javaκ°€ μžλ™μœΌλ‘œ run() λ©”μ„œλ“œλ₯Ό μ‹€ν–‰

6)  메인 μ“°λ ˆλ“œμ—μ„œ λ©”μ‹œμ§€ 좜λ ₯

System.out.println("Hello, My Runnable Child!");

 

  • 메인 μ“°λ ˆλ“œλŠ” λ³„λ„λ‘œ "Hello, My Runnable Child!"λ₯Ό 좜λ ₯
  • μ€‘μš”ν•œ 점은 μ“°λ ˆλ“œλŠ” λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ— 좜λ ₯ μˆœμ„œκ°€ μΌμ •ν•˜μ§€ μ•Šμ„ 수 있음

4.2. ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ κ²°κ³Ό

Hello, My Runnable Child!
Hello, Runnable!
Hello, Runnable!
Hello, Runnable!
...

 

  • "Hello, My Runnable Child!"λŠ” 메인 μ“°λ ˆλ“œμ—μ„œ μ‹€ν–‰
  • "Hello, Runnable!"은 μƒˆλ‘œμš΄ μ“°λ ˆλ“œμ—μ„œ 0.5μ΄ˆλ§ˆλ‹€ 반볡 좜λ ₯
  • 메인 μ“°λ ˆλ“œμ™€ MyThread2 μ“°λ ˆλ“œλŠ” λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰λœλ‹€.

4.3. Runnable을 μ‚¬μš©ν•˜λŠ” 이유

βœ… Thread 클래슀λ₯Ό μƒμ†λ°›μœΌλ©΄ λ‹€λ₯Έ 클래슀λ₯Ό 상속할 수 μ—†μ§€λ§Œ, Runnable μΈν„°νŽ˜μ΄μŠ€λŠ” κ΅¬ν˜„ν•˜λ©΄μ„œ λ‹€λ₯Έ 클래슀λ₯Ό 상속할 수 μžˆλ‹€. βœ… μ½”λ“œμ˜ ꡬ쑰가 더 μœ μ—°ν•˜κ³ , μœ μ§€λ³΄μˆ˜κ°€ μ‰¬μ›Œμ§„λ‹€. βœ… μ—¬λŸ¬ μ“°λ ˆλ“œλ₯Ό ν•˜λ‚˜μ˜ Runnable κ°μ²΄μ—μ„œ μ‹€ν–‰ν•  수 μžˆλ‹€.


4.4. 정리

Javaμ—μ„œ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜λŠ” λ°©λ²•μ—λŠ” Thread 클래슀λ₯Ό μƒμ†ν•˜λŠ” 방법과 Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법이 μžˆλ‹€. Runnable 방식은 더 μœ μ—°ν•˜κ³ , 객체지ν–₯적인 μ½”λ“œ μž‘μ„±μ„ κ°€λŠ₯ν•˜κ²Œ ν•œλ‹€. λ©€ν‹°μ“°λ ˆλ”©μ„ 잘 ν™œμš©ν•˜λ©΄ ν”„λ‘œκ·Έλž¨μ˜ 응닡성을 높이고 μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆλ‹€!

5. λΆ€λͺ¨ thread의 λŒ€κΈ°

package ch04;

public class ThreadExample4 {
	public static final void main(String[] args) {
		Runnable task = () -> {
			for (int i = 0; i <5; i++) {
				System.out.println("Hello, Lambda Runnable!");
			}
		};
		Thread thread = new Thread(task);
		thread.start();
		try {
			thread.join();
		}
		catch (InterruptedException ie) {
			System.out.println("Parent thread is interrupted");
		}
		System.out.println("Hello, My joined Child!");
	}
}

 

5.1. μžλ°”μ—μ„œ μŠ€λ ˆλ“œ

  • μŠ€λ ˆλ“œλŠ” ν”„λ‘œκ·Έλž¨μ΄ λ™μ‹œμ— μ—¬λŸ¬ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλ„λ‘ ν•˜λŠ” μ‹€ν–‰ λ‹¨μœ„
  • μžλ°” ν”„λ‘œκ·Έλž¨μ€ 기본적으둜 메인 μŠ€λ ˆλ“œ(main thread)μ—μ„œ μ‹€ν–‰
  • μΆ”κ°€λ‘œ μƒˆλ‘œμš΄ μŠ€λ ˆλ“œλ₯Ό λ§Œλ“€λ©΄, μ—¬λŸ¬ μž‘μ—…μ„ λ™μ‹œμ— μ‹€ν–‰ν•  수 있음

5.2. μ½”λ“œ 뢄석 – λΆ€λͺ¨ μŠ€λ ˆλ“œ(메인)와 μžμ‹ μŠ€λ ˆλ“œ

public static final void main(String[] args) {
    Runnable task = () -> {
        for (int i = 0; i <5; i++) {
            System.out.println("Hello, Lambda Runnable!");
        }
    };
    Thread thread = new Thread(task);
    thread.start();  // (1) μžμ‹ μŠ€λ ˆλ“œ μ‹œμž‘!

    try {
        thread.join(); // (2) λΆ€λͺ¨ μŠ€λ ˆλ“œκ°€ μžμ‹ μŠ€λ ˆλ“œκ°€ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦Ό
    } catch (InterruptedException ie) {
        System.out.println("Parent thread is interrupted");
    }

    System.out.println("Hello, My joined Child!"); // (3) μžμ‹ μŠ€λ ˆλ“œ μ’…λ£Œ ν›„ 싀행됨
}

5.3. μ‹€ν–‰ 흐름 상세 μ„€λͺ…

1️⃣ 메인 μŠ€λ ˆλ“œ(main)κ°€ 싀행됨 πŸƒ‍♂️ 메인 μŠ€λ ˆλ“œ
2️⃣ Runnable task μ •μ˜ (λžŒλ‹€μ‹) πŸƒ‍♂️ 메인 μŠ€λ ˆλ“œ
3️⃣ Thread thread = new Thread(task); (μƒˆλ‘œμš΄ μŠ€λ ˆλ“œ 객체 생성) πŸƒ‍♂️ 메인 μŠ€λ ˆλ“œ
4️⃣ thread.start(); 호좜 → μƒˆλ‘œμš΄ μŠ€λ ˆλ“œ μ‹œμž‘! πŸƒ‍♂️ 메인 μŠ€λ ˆλ“œ → πŸƒ‍♂️ μžμ‹ μŠ€λ ˆλ“œ 생성
5️⃣ μžμ‹ μŠ€λ ˆλ“œκ°€ 싀행됨 (Hello, Lambda Runnable!을 5번 좜λ ₯) πŸƒ‍♂️ μžμ‹ μŠ€λ ˆλ“œ
6️⃣ thread.join(); → 메인 μŠ€λ ˆλ“œλŠ” μžμ‹ μŠ€λ ˆλ“œκ°€ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦Ό(μΌμ‹œ μ •μ§€) πŸƒ‍♂️ 메인 μŠ€λ ˆλ“œ(λŒ€κΈ°)
7️⃣ μžμ‹ μŠ€λ ˆλ“œ μ’…λ£Œ πŸƒ‍♂️ μžμ‹ μŠ€λ ˆλ“œ μ’…λ£Œ
8️⃣ Hello, My joined Child! 좜λ ₯ πŸƒ‍♂️ 메인 μŠ€λ ˆλ“œ λ‹€μ‹œ μ‹€ν–‰

5.4. join()이 μ—†λ‹€λ©΄?

λ§Œμ•½ thread.join();을 μ‚­μ œν•˜λ©΄, 메인 μŠ€λ ˆλ“œλŠ” 기닀리지 μ•Šκ³  λ°”λ‘œ λ‹€μŒ μ½”λ“œ(System.out.println("Hello, My joined Child!");)λ₯Ό μ‹€ν–‰. 그럼 μ‹€ν–‰ κ²°κ³Όκ°€ μ„žμ—¬μ„œ λ‚˜μ˜¬ μˆ˜λ„ 있음

βœ… join() μžˆλŠ” 경우 (μžμ‹ μŠ€λ ˆλ“œκ°€ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦Ό)

Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, My joined Child!  // πŸ”₯ μžμ‹ μŠ€λ ˆλ“œκ°€ λλ‚œ ν›„ 좜λ ₯됨

 

❌  join() μ—†λŠ” 경우 ( 좜λ ₯ μˆœμ„œκ°€ μ„žμΌ κ°€λŠ₯μ„± λ†’μŒ )

Hello, Lambda Runnable!
Hello, My joined Child!  // ⚠️ λΆ€λͺ¨ μŠ€λ ˆλ“œκ°€ λ¨Όμ € 싀행될 μˆ˜λ„ 있음
Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!

5. thread의 μ’…λ£Œ: Interrupt

interrupt() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μžμ‹ μŠ€λ ˆλ“œλ₯Ό 쀑단할 수 μžˆλ‹€. μ•„λž˜λŠ” 이λ₯Ό 확인할 수 μžˆλŠ” μ˜ˆμ œμ™€ μ„€λͺ…이닀.

5.1. μ½”λ“œ μ„€λͺ…

public class ThreadExample5 {
    public static void main(String[] args) throws InterruptedException {
        // (1) μžμ‹ μŠ€λ ˆλ“œμ˜ μž‘μ—… μ •μ˜
        Runnable task = () -> {
            try {
                while (true) {
                    System.out.println("Hello, Lambda Runnable!");  // (2) 0.1μ΄ˆλ§ˆλ‹€ 좜λ ₯
                    Thread.sleep(100);  // (3) 0.1초 λŒ€κΈ°
                }
            }
            catch (InterruptedException ie) {  // (4) InterruptedException μ˜ˆμ™Έ 처리
                System.out.println("I'm Interrupted");  // (5) μΈν„°λŸ½νŠΈ μ‹œ 좜λ ₯
            }
        };

        // (6) μƒˆλ‘œμš΄ μŠ€λ ˆλ“œ 생성
        Thread thread = new Thread(task);  // task 싀행을 μœ„ν•œ μƒˆλ‘œμš΄ μŠ€λ ˆλ“œ
        thread.start();  // (7) μžμ‹ μŠ€λ ˆλ“œ μ‹œμž‘

        Thread.sleep(500);  // (8) 메인 μŠ€λ ˆλ“œ 0.5초 λŒ€κΈ°
        thread.interrupt();  // (9) μžμ‹ μŠ€λ ˆλ“œμ— μΈν„°λŸ½νŠΈ μ‹ ν˜Έ 보냄
        System.out.println("Hello, My interrupted Child!");  // (10) 메인 μŠ€λ ˆλ“œμ˜ 좜λ ₯
    }
}

5.2. ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ κ²°κ³Ό

Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!
Hello, Lambda Runnable!
I'm Interrupted
Hello, My interrupted Child!

 

6. Multicore ν”„λ‘œκ·Έλž˜λ°

Multicore μ‹œμŠ€ν…œμ—μ„œλŠ” μ—¬λŸ¬ 개의 ν”„λ‘œμ„Έμ„œ μ½”μ–΄κ°€ λ™μ‹œμ— μž‘μ—…μ„ μ²˜λ¦¬ν•  수 μžˆμ–΄, 병렬 μ‹€ν–‰(Parallel Execution)을 톡해 μ„±λŠ₯을 κ·ΉλŒ€ν™”ν•  수 있음. ν•˜μ§€λ§Œ Multithreading은 μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ—, λ‹€μ–‘ν•œ λ¬Έμ œλ“€μ΄ λ°œμƒν•  수 있음.

1. μ£Όμš” λ¬Έμ œλ“€

  • Task 식별: λ³‘λ ¬λ‘œ μ‹€ν–‰ν•  수 μžˆλŠ” μž‘μ—…μ„ κ΅¬λΆ„ν•˜λŠ” 것이 μ€‘μš”
  • κ· ν˜• 문제: 각 μž‘μ—…μ˜ 처리 μ‹œκ°„κ³Ό λ¦¬μ†ŒμŠ€λ₯Ό κ· ν˜• 있게 λΆ„λ°°ν•΄μ•Ό 함
  • 데이터 λΆ„ν• : 효율적인 데이터 뢄할이 ν•„μš”
  • 데이터 μ˜μ‘΄μ„±: μž‘μ—… κ°„ 데이터 μ˜μ‘΄μ„±μ„ ν•΄κ²°ν•΄μ•Ό 함
  • 디버깅 어렀움: μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μ‹€ν–‰λ˜μ–΄ 디버깅이 μ–΄λ €μ›Œμ§

2. 병렬 처리의 두 κ°€μ§€ μœ ν˜•

  • 데이터 병렬성: λ™μΌν•œ μž‘μ—…μ„ μ—¬λŸ¬ 데이터 μ„ΈνŠΈμ— λŒ€ν•΄ λ™μ‹œμ— 처리.
  • μž‘μ—… 병렬성: μ„œλ‘œ λ‹€λ₯Έ μž‘μ—…μ„ λ™μ‹œμ— μ‹€ν–‰.

3. λΆ„μ‚° μ‹œμŠ€ν…œκ³Ό ν΄λΌμš°λ“œ

ν˜„λŒ€ μ‹œμŠ€ν…œμ—μ„œλŠ” λΆ„μ‚° μ‹œμŠ€ν…œκ³Ό ν΄λΌμš°λ“œλ₯Ό 톡해 μ—¬λŸ¬ μ„œλ²„κ°€ λ³‘λ ¬λ‘œ μž‘μ—…μ„ λΆ„λ‹΄ν•©λ‹ˆλ‹€. μ΄λŠ” Multicore μ‹œμŠ€ν…œμ„ λ„˜μ–΄μ„œλŠ” λΆ„μ‚° 처리 ν™˜κ²½μ„ μ˜λ―Έν•˜λ©°, 데이터 μ˜μ‘΄μ„±κ³Ό κ· ν˜•μ„ λ§žμΆ”λŠ” 것이 μ€‘μš”

 

6. Amdahl's의 곡식

μ½”μ–΄λŠ” 무쑰건 λ§Žμ„μˆ˜λ‘ μ’‹μ„κΉŒ?

 

Amdahl's 법칙은 병렬 μ²˜λ¦¬μ—μ„œ μ„±λŠ₯ ν–₯μƒμ˜ ν•œκ³„λ₯Ό μ„€λͺ…함. μ½”μ–΄ μˆ˜κ°€ λ§Žμ•„μ§ˆμˆ˜λ‘ μ„±λŠ₯이 λΉ„λ‘€ν•΄ μ¦κ°€ν•˜λŠ” 것이 μ•„λ‹ˆλΌ, 병렬 μ²˜λ¦¬ν•  수 μžˆλŠ” λΆ€λΆ„μ˜ ν•œκ³„λ‘œ 인해 μ„±λŠ₯ ν–₯상에 ν•œκ³„κ°€ 쑴재.

Amdahl's 법칙 곡식:

Stotal=1(1−P)+PNS_{total} = \frac{1}{(1 - P) + \frac{P}{N}}

  • S_total: 전체 μ„±λŠ₯ ν–₯상 λΉ„μœ¨
  • P: 병렬화가 κ°€λŠ₯ν•œ λΆ€λΆ„μ˜ λΉ„μœ¨
  • N: μ‚¬μš©ν•˜λŠ” ν”„λ‘œμ„Έμ„œ(μ½”μ–΄)의 수

핡심 포인트

  • μ½”μ–΄κ°€ λ§Žλ‹€κ³  4λ°° 빨라지지 μ•ŠλŠ”λ‹€: 예λ₯Ό λ“€μ–΄, 병렬화가 κ°€λŠ₯ν•œ 뢀뢄이 75%라면, 4개의 μ½”μ–΄λ‘œλŠ” μ„±λŠ₯이 μ•½ 3λ°° ν–₯상. λ‚˜λ¨Έμ§€ 25%의 순차적 처리 뢀뢄은 μ—¬μ „νžˆ ν•œ μ½”μ–΄μ—μ„œ μ²˜λ¦¬λ˜μ–΄ 병렬 처리의 μ„±λŠ₯ ν–₯상에 μ œν•œμ„ 쀌.
  • 병렬 처리의 ν•œκ³„: 병렬 μ²˜λ¦¬κ°€ κ°€λŠ₯ν•œ 뢀뢄이 λ§Žμ§€ μ•ŠμœΌλ©΄, μ½”μ–΄ μˆ˜κ°€ 아무리 λ§Žμ•„λ„ μ„±λŠ₯ ν–₯상이 μ œν•œμ .