๐Ÿ—„๏ธ Backend/Java

์ž๋ฐ”(Java) - Thread(๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ)

kongmi 2023. 2. 2. 20:34

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ(Multi Thread)?

  • ํ•˜๋‚˜์˜ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ผ์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ
When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon.

Main Thread

๐Ÿ’ก๋ชจ๋“  ์ž๋ฐ” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ Main Thread๊ฐ€ main() ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด์„œ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก์Šค๋ ˆ๋“œ๋Š” ์šด์˜์ฒด์ œ๊ฐ€ ์‹คํ–‰์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„์˜ ํ…€์ด ์žˆ์Œ

์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด ํ”„๋กœ์„ธ์Šค๋„ ์ข…๋ฃŒ๋˜์ง€๋งŒ,

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ข…๋ฃŒ๋˜๋”๋ผ๋„ ์‹คํ–‰ ์ค‘์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ๋‹ค๋ฉด ํ”„๋กœ์„ธ์Šค๋Š” ์ข…๋ฃŒ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Thread ์ƒ์„ฑ

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. ๐Ÿ’กThis subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. 

Thread ํด๋ž˜์Šค ์ƒ์†

๐Ÿšจ์ œ์ผ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์•„๋‹˜. ๋‹ค๋ฅธ ํด๋ž˜์Šค ์ƒ์†์„ ๋ชป ๋ฐ›์Œ

public class Main {
    public static void main(String[] args) {
        Thread test = new TestThread();
        test.start();
        int sum = 0;

        for (int i = 0; i <= 100; i++) {
            sum += i;
            System.out.println("์—ฌ๊ธฐ๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ์ž…๋‹ˆ๋‹ค." + sum);
        }
        System.out.println(Thread.currentThread() + "ํ•ฉ๊ณ„ : " + sum);
    }
}

class TestThread extends Thread {
    @Override
    public void run() {
        int sum = 0;
        for(int i = 0; i <= 100; i++) {
            sum += i;
            System.out.println("์—ฌ๊ธฐ๋Š” ํ…Œ์ŠคํŠธ ์Šค๋ ˆ๋“œ ์ž…๋‹ˆ๋‹ค. : " + sum);
        }
        System.out.println(Thread.currentThread() + "ํ•ฉ๊ณ„ : " + sum);
    }
}

Runnable ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„

  • new๋ฅผ ํ†ตํ•ด Thread ํด๋ž˜์Šค ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ ํ›„ start() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ํ•  ์ž‘์—…์„ ํ• ๋‹นํ•˜๋Š” ๋ฐฉ๋ฒ•
  1. Thread ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋Š” Runnable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค ๊ฐ์ฒด๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  2. start() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ž‘์—… ์Šค๋ ˆ๋“œ๋Š” ์ž์‹ ์˜ run() ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ ํ•ฉ๋‹ˆ๋‹ค.
import static java.lang.Thread.sleep;

public class RunThread implements Runnable {
    @Override
    public void run() {
        int sum = 0;
        for(int i = 0; i < 5; i++) {
            sum += i;
            try {
                sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("" + Thread.currentThread() + sum);
        }
        System.out.println(Thread.currentThread() + " ํ•ฉ๊ณ„ : " + sum);
    }
}
public class Main {
    public static void main(String[] args) {
    	Runnable runTh = new RunThread();
        Thread th1 = new Thread(runTh);
        runTh1.start();
        
        Runnable runTh2 = new RunThread();
        Thread th2 = new Thread(runTh2);
        runTh2.start();
    }
}
Runnabe runTh = new RunThread();
์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ์ž‘์—… ๋‚ด์šฉ์€ ํฌํ•จํ•˜๊ณ  ์žˆ์ง€๋งŒ ์‹ค์ œ ์Šค๋ ˆ๋“œ๋Š” ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— Thread ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ์‹œ Runnable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด์ค€ ํ›„ start() ํ•ด์•ผ ํ•œ๋‹ค.
Thread[Thread-0,5,main]0
Thread[Thread-1,5,main]0
Thread[Thread-0,5,main]1
Thread[Thread-1,5,main]1
Thread[Thread-1,5,main]3
Thread[Thread-0,5,main]3
Thread[Thread-0,5,main]6
Thread[Thread-1,5,main]6
Thread[Thread-1,5,main]10
Thread[Thread-0,5,main]10
Thread[Thread-1,5,main] ํ•ฉ๊ณ„ : 10
Thread[Thread-0,5,main] ํ•ฉ๊ณ„ : 10

์ต๋ช…์˜ ๊ฐ์ฒด ์ƒ์„ฑ (์ผํšŒ์šฉ)

Runnable task = new Runnable() {
            @Override
            public void run() {
                int sum = 0;
                for(int i = 0 ; i < 10; i++) {
                    sum += i;
                    System.out.println("" + Thread.currentThread() + sum);
                }
                System.out.println(Thread.currentThread() + " ํ•ฉ๊ณ„ : " + sum);
            }
        }; // ๊ตฌํ˜„๋ถ€์˜ ๋์ด๋‹ค ์˜๋ฏธ๋กœ ';' ๋ถ™์—ฌ์•ผ ํ•จ.
        Thread runTh3 = new Thread(task);
        runTh3.start();

์ต๋ช…์˜ ๊ฐ์ฒด๋ฅผ ๋žŒ๋‹ค์‹์œผ๋กœ ์ƒ์„ฑ

Runnable task2 = () -> {
            int sum = 0;
            for(int i = 0 ; i < 10; i++) {
                sum += i;
                System.out.println("" + Thread.currentThread() + sum);
            }
            System.out.println(Thread.currentThread() + " ํ•ฉ๊ณ„ : " + sum);
        };
        Thread runTh4 = new Thread(task2);
        runTh4.start();

        System.out.println("๋‚˜๋Š” main ์ž…๋‹ˆ๋‹ค.");

Thread ์šฐ์„  ์ˆœ์œ„

์šฐ์„  ์ˆœ์œ„(Priority) ๋ฐฉ์‹

์šฐ์„  ์ˆœ์œ„๊ฐ€ ๋†’์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์‹คํ–‰์„ ๋” ๋งŽ์ด ํ•˜๋„๋ก ์Šค์ผ€์ค„๋ง ํ•˜๋Š” ๋ฐฉ๋ฒ•

  • thread.setPriority(1) : ์šฐ์„  ์ˆœ์œ„ ๊ฐ€์žฅ ๋‚ฎ์Œ
  • thread.setPriority(10) : ์šฐ์„  ์ˆœ์œ„ ๊ฐ€์žฅ ๋†’์Œ

์ˆœํ™˜ ํ• ๋‹น ๋ฐฉ์‹

  • ์‹œ๊ฐ„ ํ• ๋‹น๋Ÿ‰์„ ์ •ํ•ด์„œ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์ •ํ•ด์ง„ ์‹œ๊ฐ„๋งŒํผ๋งŒ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ํ•ด๋‹น ๋ฐฉ์‹์€ JVM ์•ˆ์—์„œ ์ด๋ฃจ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์ œ์–ดํ•  ์ˆ˜ ์—†์Œ.

Thread ์ƒํƒœ

์Šค๋ ˆ๋“œ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์šฐ์„  ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.

์‹คํ–‰๋Œ€๊ธฐ ์ƒํƒœ๋ž€ ์ฝ”์–ด์—์„œ ์ž‘์—…์„ ํ• ๋‹น๋ฐ›์ง€ ๋ชปํ•œ ์ƒํƒœ ์ž…๋‹ˆ๋‹ค.

 

์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ์— ์žˆ๋Š” ์Šค๋ ˆ๋“œ ์ค‘ ์Šค๋ ˆ๋“œ ์Šค์ผ€์ค„๋ง์œผ๋กœ ์„ ํƒ๋œ ์Šค๋ ˆ๋“œ๊ฐ€ CPU๋ฅผ ์ ์œ ํ•˜๊ณ  ์šด์˜์ฒด์ œ๊ฐ€ run()์„ ์‹คํ–‰ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋•Œ CPU๋ฅผ ์ ์œ ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ๊ฐ€ ์‹คํ–‰ ์ƒํƒœ ์ž…๋‹ˆ๋‹ค.

 

run() ๋ฉ”์†Œ๋“œ๊ฐ€ ๋๋‚˜์ง€ ์•Š์•„๋„ ์Šค๋ ˆ๋“œ ์Šค์ผ€์ค„๋ง์— ์˜ํ•ด ๋‹ค์‹œ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋Œ์•„๊ฐ€๋ฉฐ ๋ฒˆ๊ฐˆ์•„ ๊ฐ€๋ฉด์„œ ๋‚ด์šฉ์„ ๋๋‚ผ ๋•Œ๊นŒ์ง€ ์ง€์†ํ•ฉ๋‹ˆ๋‹ค.

๊ฒฝ์šฐ์— ๋”ฐ๋ผ run() ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰์ค‘์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์ผ์‹œ ์ •์ง€ ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๋Š”๋ฐ ์ผ์‹œ ์ •์ง€ ํ›„์—๋Š” ๋‹ค์‹œ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๊ฐ‘๋‹ˆ๋‹ค.

Thread ์ƒํƒœ ์ œ์–ด

  • ์‹คํ–‰ ์ค‘์ธ ์Šค๋ ˆ๋“œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ œ์–ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฉ€ํ‹ฐ ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ •๊ตํ•œ ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”์†Œ๋“œ ์„ค๋ช…
interrupt() ์ผ์‹œ ์ •์ง€ ์ƒํƒœ์˜ ์Šค๋ ˆ๋“œ์—์„œ interruptException ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ, ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ฝ”๋“œ
notify(), notifyAll() ๋™๊ธฐํ™” ๋ธ”๋ก ๋‚ด์—์„œ wait() ๋ฉ”์†Œ๋“œ์— ์˜ํ•ด ์ผ์‹œ ์ •์ง€ ์ƒํƒœ์— ์žˆ๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋งŒ๋“ฌ
sleep() ์ฃผ์–ด์ง„ ์‹œ๊ฐ„ ๋™์•ˆ ์Šค๋ ˆ๋“œ๋ฅผ ์ผ์‹œ ์ •์ง€ ์ƒํƒœ๋กœ ๋งŒ๋“ฌ
join() join() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ์Šค๋ ˆ๋“œ๋Š” ์ผ์‹œ ์ •์ง€ ์ƒํƒœ๊ฐ€ ๋œ๋‹ค. ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๊ฐ€๋ ค๋ฉด join() ๋ฉ”์†Œ๋“œ๋ฅผ ๋ฉค๋ฒ„๋กœ ๊ฐ€์ง„ ์Šค๋ ˆ๋“œ๊ฐ€ ์ข…๋ฃŒ ๋˜๊ฑฐ๋‚˜, ๋งค๊ฐœ๊ฐ’์œผ๋กœ ์ฃผ์–ด์ง„ ์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ ํ•จ
wait() ๋™๊ธฐํ™” ๋ธ”๋ก ๋‚ด์—์„œ ์Šค๋ ˆ๋“œ๋ฅผ ์ผ์‹œ ์ •์ง€ ์ƒํƒœ๋กœ ๋งŒ๋“ฌ
* ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์ฃผ์–ด์ง€๋ฉด ์ฃผ์–ด์ง„ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ์ž๋™์œผ๋กœ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๊ฐ€ ๋จ.
* ์‹œ๊ฐ„์ด ์ฃผ์–ด์ง€์ง€ ์•Š์œผ๋ฉด notify(), notifyAll() ๋ฉ”์†Œ๋“œ์— ์˜ํ•ด ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ์ „ํ™˜

์ฃผ์–ด์ง„ ์‹œ๊ฐ„ ๋™์•ˆ ์ผ์‹œ ์ •์ง€(sleep())

  • ์‹คํ–‰์ค‘์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์ผ์ • ์‹œ๊ฐ„๋™์•ˆ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋งŒ๋“ฌ
  • ๋ฐ˜๋ณต ์ˆ˜ํ–‰์— ๋Œ€ํ•œ ์‹œ๊ฐ„์„ ๋Šฆ์ถœ ๋•Œ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์ธํ„ฐ๋ŸฝํŠธ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ ์‚ฌ์šฉ
import java.awt.*;
import static java.lang.Thread.sleep;

public class Main {
    public static void main(String[] args) {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        for(int i = 0; i < 10; i++) {
            toolkit.beep();
            try {
                sleep(3000);
            } catch (InterruptedException e) {
            }
        }
    }
}
3์ดˆ ์ฃผ๊ธฐ๋กœ 10๋ฒˆ ๋น„ํ”„์Œ ๋ฐœ์ƒํ•˜๋Š” ์ฝ”๋“œ

๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆผ(join())

  • ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉ
    ์˜ˆ) ์˜ˆ๋ฅผ ๋“ค์–ด ๊ณ„์‚ฐ ์ž‘์—…์„ ํ•˜๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ๋ชจ๋“  ๊ณ„์‚ฐ์„ ๋งˆ์น˜๊ณ  ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ›์•„ ์ด์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

๐Ÿถ join() ์‚ฌ์šฉ ์ „

public class Main {
    public static void main(String[] args) {
       SumTh sumTh = new SumTh();
       sumTh.start();
       
       System.out.println("ํ•ฉ : " + sumTh.getSum());
    }
}

class SumTh extends Thread {
    private long sum;

    public long getSum() {
        return sum;
    }

    public void setSum(long sum) {
        this.sum = sum;
    }

    @Override
    public void run() {
        for(int i = 0; i < 100; i++) {
            sum += 1;
            try {
                sleep(1);
            } catch (InterruptedException e) {}
        }
    }
}
ํ•ฉ : 0
Thread๊ฐ€ ์‹คํ–‰ ๋˜๊ธฐ ๊นŒ์ง€ ์‹œ๊ฐ„๋ณด๋‹ค main์—์„œ getSum() ์‹คํ–‰ํ•˜๋Š” ์‹œ๊ฐ„์ด ํ›จ์”ฌ ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— for๋ฌธ ๋Œ๊ธฐ ์ „์— ๋๋‚˜์„œ 0์œผ๋กœ ๋‚˜์˜ด.

๐Ÿ’ก join()์— ๋ฌดํ•œ๋Œ€๊ธฐ(์ธ์ž์— ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ์”€) ๊ฑธ๊ฑฐ๋‚˜ ์‹œ๊ฐ„์„ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ์Œ. ์˜ˆ) join(200), join() …

 

๐Ÿถ join() ์‚ฌ์šฉ ํ›„

public class Main {
    public static void main(String[] args) {
       SumTh sumTh = new SumTh();
       sumTh.start();
       try {
           sumTh.join(); // sumTh๊ฐ€ ์ข…๋ฃŒ ๋  ๋•Œ ๊นŒ์ง€ main์ด ๋Œ€๊ธฐ ํ•˜๊ณ  ์žˆ์Œ
       } catch (InterruptedException e) {}
       System.out.println("ํ•ฉ : " + sumTh.getSum());
    }
}

class SumTh extends Thread {
    private long sum;

    public long getSum() {
        return sum;
    }

    public void setSum(long sum) {
        this.sum = sum;
    }

    @Override
    public void run() {
        for(int i = 0; i < 100; i++) {
            sum += 1;
            try {
                sleep(1);
            } catch (InterruptedException e) {}
        }
    }
}
ํ•ฉ : 100

์Šค๋ ˆ๋“œ ๊ฐ„ ํ˜‘์—… (wait(), notify(), nofifyAll())

  • ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋‘ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ ์‹คํ–‰ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž‘์—…์„ ์™„๋ฃŒํ•˜๋ฉด notify() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ผ์‹œ ์ •์ง€ ์ƒํƒœ์— ์žˆ๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋ฅผ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋งŒ๋“ค๊ณ , ์ž์‹ ์€ ๋‘๋ฒˆ์งธ ์ž‘์—…์„ ํ•˜์ง€ ์•Š๋„๋ก wait() ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœ ํ•ฉ๋‹ˆ๋‹ค.
  • notifyAll() ๋ฉ”์†Œ๋“œ๋Š” wait()์— ์˜ํ•ด ์ผ์‹œ ์ •์ง€๋œ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๋ฅผ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

Main

public class Main {
    public static void main(String[] args) {
        WorkObject sharedObject = new WorkObject();
        ThreadA thA = new ThreadA(sharedObject);
        ThreadB thB = new ThreadB(sharedObject);
        thA.start();
        thB.start();
    }
}

WorkObject

public class WorkObject {
    public synchronized void methodA() {
        System.out.println("Thread์˜ methodA() ์ž‘์—… ์‹คํ–‰");
        notify(); // ์ผ์‹œ์ •์ง€ ์ƒํƒœ์ธ ThreadB๋ฅผ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ์ „ํ™˜
        try {
            wait(); // ThreadA๋ฅผ ์ผ์‹œ ์ •์ง€ ์ƒํƒœ๋กœ ๋งŒ๋“ฌ
        } catch(InterruptedException e) {}

    }
    public synchronized void methodB() {
        System.out.println("Thread์˜ methodB() ์ž‘์—… ์‹คํ–‰");
        notify(); // ์ผ์‹œ์ •์ง€ ์ƒํƒœ์ธ ThreadA๋ฅผ ์‹คํ–‰ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ์ „ํ™˜
        try {
            wait(); // ThreadB๋ฅผ ์ผ์‹œ ์ •์ง€ ์ƒํƒœ๋กœ ๋งŒ๋“ฌ
        } catch(InterruptedException e) {}
    }
}
์Šค๋ ˆ๋“œ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค ์ƒ์„ฑ
synchronized : ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋™์‹œ ์ ‘์†์ด ํ—ˆ์šฉ์ด ๋˜์ง€ ์•Š๋„๋ก lock์„ ๊ฑฐ๋Š” ์ž‘์—…

ThreadA

public class ThreadA extends Thread {
    private WorkObject workObject;

    public ThreadA(WorkObject workObject) {
        this.workObject = workObject;
    }
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            workObject.methodA();
        }
    }
}

ThreadB

public class ThreadB extends Thread {
    private WorkObject workObject;

    public ThreadB(WorkObject workObject) {
        this.workObject = workObject;
    }

    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            workObject.methodB();
        }
    }
}

์Šค๋ ˆ๋“œ์˜ ์•ˆ์ „ํ•œ ์ข…๋ฃŒ (stop ํ”Œ๋ž˜๊ทธ, interrupt())

  • ์Šค๋ ˆ๋“œ์˜ ์•ˆ์ „ํ•œ ์ข…๋ฃŒ ๋ฐฉ๋ฒ•์€ stop ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ interrupt() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

๐Ÿถ stop ํ”Œ๋ž˜๊ทธ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

Main

public class Main {
    public static void main(String[]args) throws InterruptedException {
        RunThread runThread = new RunThread();
        runThread.start();
        Thread.sleep(1000);
        runThread.setStop(true);
    }
}

RunThread

public class RunThread extends Thread {
    private boolean stop; // stop ํ”Œ๋ž˜๊ทธ ์„ค์ •

    public void setStop(boolean stop) {
        this.stop = stop;
    }

    public void run() {
        while(!stop) {
            System.out.println("Thread ์‹คํ–‰ ์ค‘...");
        }
        System.out.println("์ž์› ์ •๋ฆฌ");
        System.out.println("์‹คํ–‰ ์ข…๋ฃŒ");
    }
}

๐Ÿถ interrupt() ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

  • interrupt() ๋ฉ”์†Œ๋“œ๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์ผ์‹œ ์ •์ง€ ์ƒํƒœ์— ์žˆ์„ ๋•Œ InterruptException ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.
  • ์ด๊ฒƒ์„ ์ด์šฉํ•˜์—ฌ run() ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์ƒ ์ข…๋ฃŒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
public class Main {
    public static void main(String[]args) {
        InterruptThread interThread = new InterruptThread();
        interThread.start();
        try {
            Thread.sleep(1000);
        } catch(InterruptedException e) { }
        interThread.interrupt();
    }
}
public class InterruptThread extends Thread {
    @Override
    public void run() {
        try {
            while (true) {
                System.out.println("์Šค๋ ˆ๋“œ ์‹คํ–‰ ์ค‘.....");
                sleep(1); // interrupt ๊ฑธ๋ฆด ์ˆ˜ ์žˆ๋Š” ํƒ€์ด๋ฐ (while(true) ํƒˆ์ถœ ์กฐ๊ฑด)
            }
        } catch(InterruptedException e) {
            System.out.println("์ธํ„ฐ๋ŸฝํŠธ ๋ฐœ์ƒ......");
        }
        System.out.println("์ข…๋ฃŒ๋ฅผ ์œ„ํ•ด ์ž์› ์ •๋ฆฌ ์ค‘.....");
        System.out.println("์‹คํ–‰ ์ข…๋ฃŒ");
    }
}

๋™๊ธฐํ™” (synchronized)

  • ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ์—์„œ ํŠน์ • ๋ฉ”์†Œ๋“œ๋‚˜ ๋ธ”๋ก{}์— ๋Œ€ํ•ด lock(์ž ๊ธˆ)์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ
  • ๋™์‹œ ์ ‘๊ทผ์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋ฉฐ, ์‚ฌ์šฉ์‹œ ๊ธ‰๊ฒฉํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
    โžก๏ธ ์ ์žฌ ์ ์†Œ์— ์ž˜ ์‚ฌ์šฉํ•ด์•ผ ํ•จ

๐Ÿถ synchronized ํ•˜๊ธฐ ์ „..

import static java.lang.Thread.sleep;

public class Main {
    public static void main(String[] args) {
        SharedValue sharedValue = new SharedValue();
        // Runnable ์ธํ„ฐํŽ˜์ด์Šค ์ต๋ช…์˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
        Runnable th1 = new Runnable() {
            @Override
            public void run() {
                sharedValue.setValue(100);
            }
        };
        Thread thread1 = new Thread(th1);
        thread1.setName("์Šค๋ ˆ๋“œ 1"); // ์Šค๋ ˆ๋“œ ์ด๋ฆ„์„ ์„ค์ •
        thread1.start();

        // Runnable ์ธํ„ฐํŽ˜์ด์Šค ๋žŒ๋‹ค์‹์œผ๋กœ ์ต๋ช…์˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
        Runnable th2 = () -> {
          sharedValue.setValue(200);
        };
        Thread thread2 = new Thread(th2);
        thread2.setName("์Šค๋ ˆ๋“œ 2");
        thread2.start();
    }
}

class SharedValue {
    private int value = 0;

    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
        try {
            sleep(2000);
        } catch(InterruptedException e) {
        }
        System.out.println(Thread.currentThread().getName() + "์˜ ๊ฐ’ : " + getValue());
    }
}
์Šค๋ ˆ๋“œ 1์˜ ๊ฐ’ : 200
์Šค๋ ˆ๋“œ 2์˜ ๊ฐ’ : 200
synchronized ์•ˆํ•˜๋ฉด ๊ฐ’์ด ๊นจ์ง.
public void setValue(int value) {}

๐Ÿถ synchronized ์ ์šฉ ํ›„

import static java.lang.Thread.sleep;

public class Main {
    public static void main(String[] args) {
        SharedValue sharedValue = new SharedValue();
        // Runnable ์ธํ„ฐํŽ˜์ด์Šค ์ต๋ช…์˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
        Runnable th1 = new Runnable() {
            @Override
            public void run() {
                sharedValue.setValue(100);
            }
        };
        Thread thread1 = new Thread(th1);
        thread1.setName("์Šค๋ ˆ๋“œ 1"); // ์Šค๋ ˆ๋“œ ์ด๋ฆ„์„ ์„ค์ •
        thread1.start();

        // Runnable ์ธํ„ฐํŽ˜์ด์Šค ๋žŒ๋‹ค์‹์œผ๋กœ ์ต๋ช…์˜ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
        Runnable th2 = () -> {
          sharedValue.setValue(200);
        };
        Thread thread2 = new Thread(th2);
        thread2.setName("์Šค๋ ˆ๋“œ 2");
        thread2.start();
    }
}

class SharedValue {
    private int value = 0;

    public int getValue() {
        return value;
    }
    public synchronized void setValue(int value) { // synchronized ์•ˆํ•˜๋ฉด ๊ฐ’์ด ๊นจ์ง.
        this.value = value;
        try {
            sleep(2000);
        } catch(InterruptedException e) {
        }
        System.out.println(Thread.currentThread().getName() + "์˜ ๊ฐ’ : " + getValue());
    }
}
์Šค๋ ˆ๋“œ 1์˜ ๊ฐ’ : 100
์Šค๋ ˆ๋“œ 2์˜ ๊ฐ’ : 200

๐Ÿ’ก Thread ์ด๋ฆ„ ์ƒ์„ฑ

๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ๋Š” thread.getName()์„ ํ†ตํ•ด Thread-n์ด๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์กฐ๊ธˆ ๋” ์ˆ˜์›”ํ•œ ๋””๋ฒ„๊น…์„ ์œ„ํ•ด thread.setName("์Šค๋ ˆ๋“œ ์ด๋ฆ„")์„ ํ†ตํ•ด ์ง์ ‘ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

subThread1.setName("ํ…Œ์ŠคํŠธ ์“ฐ๋ ˆ๋“œ");
subThread1.start();

โญ๋ฐ๋ชฌ ์Šค๋ ˆ๋“œ(daemon thread)

  • ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ์ž‘์—…์„ ๋•๋Š” ๋ณด์กฐ ์Šค๋ ˆ๋“œ
  • ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ชจ๋‘ ์ข…๋ฃŒ๋˜๋ฉด ๋ฐ๋ชฌ ์Šค๋ ˆ๋“œ๋„ ๊ฐ•์ œ ์ข…๋ฃŒ ๋จ
  • start() ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ์ „์— setDaemon(true)๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•จโ—
public class Main {
    public static void main(String[] args) throws InterruptedException {
        AutoSaveThread auto = new AutoSaveThread();
        auto.setDaemon(true); // ๋ฐ๋ชฌ์Šค๋ ˆ๋“œ ์„ค์ •
        auto.start();
        sleep(30000);
    }
}

class AutoSaveThread extends Thread {
     public void save() {
         System.out.println("์ž‘์—… ๋‚ด์šฉ์„ ์ €์žฅํ•จ");
     }
     @Override
     public void run() {
         while(true) {
             try {
                 sleep(3000);
             } catch (InterruptedException e) {
             }
             save();
         }
     }
}