πŸ’» Programming/Java

[Java] μžλ°” λ©€ν‹°μ“°λ ˆλ“œ (Multi Thread)

μΌ€μ΄μΉ˜ 2020. 4. 17. 13:01

μžλ°” λ©€ν‹°μ“°λ ˆλ“œ κ΅¬ν˜„ν•˜κΈ°

μžλ°”μ—μ„œ μ“°λ ˆλ“œλ₯Ό μ΄μš©ν•˜μ—¬ λΉ„λ™κΈ°λ‘œ μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” 방법에 λŒ€ν•΄μ„œ κ°„λž΅ν•˜κ²Œ ν¬μŠ€νŒ…ν•©λ‹ˆλ‹€.

 

μžλ°”μ—μ„œ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜λŠ” 방법은 2가지가 μžˆμŠ΅λ‹ˆλ‹€.

첫 λ²ˆμ§ΈλŠ” Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜μ—¬ Thread μƒμ„±μžλ‘œ ν•΄λ‹Ή κ΅¬ν˜„μ²΄λ₯Ό λ„˜κ²¨μ£ΌλŠ” 방법이고,

두 λ²ˆμ§ΈλŠ” 직접 Thread 클래슀λ₯Ό μƒμ†ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

 

μš°μ„  첫 번째 λ°©λ²•μœΌλ‘œ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜μ—¬  μ‹€ν–‰ν•˜λŠ” μ½”λ“œλ₯Ό λ³΄κ² μŠ΅λ‹ˆλ‹€.

    public static void main(String[] args) {
        Runnable task = new Task();
        Thread thread = new Thread(task);
        thread.start();
    }
    static class Task implements Runnable {

        int num = 0;

        @Override
        public void run() {
        	for(int i = 0; i < 10; i++) {
	            System.out.println(num++);
            }
        }
    }

 

μœ„ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ μ•„λž˜μ™€ 같이 좜λ ₯이 λ©λ‹ˆλ‹€.

	0
	1
	2
	3
	4
	5
	6
	7
	8
	9

 

μ΄λ²ˆμ—λŠ” Thread 클래슀λ₯Ό μƒμ†ν•˜μ—¬ λ™μΌν•œ μž‘μ—…(task)을 ν•˜λ„λ‘ ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

    public static void main(String[] args) {
        ThreadTask task = new ThreadTask();
        task.run();        
    }
    static class ThreadTask extends Thread {

        int num = 0;

        @Override
        public void run() {
        	for(int i = 0; i < 10; i++) {
	            System.out.println(num++);
    	    }
        }
    }

 

μ‹€ν–‰ν•˜λ©΄ Runnable둜 κ΅¬ν˜„ν–ˆμ„ λ•Œμ™€ λ™μΌν•˜κ²Œ μ•„λž˜μ™€ 같이 좜λ ₯이 λ©λ‹ˆλ‹€.

	0
	1
	2
	3
	4
	5
	6
	7
	8
	9

 

참고둜 μ‹€μ œ Thread 클래슀λ₯Ό 열어보면 Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό implementν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

public class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name;
    private int            priority;
    private Thread         threadQ;
    private long           eetop;
.
.
.

 

그럼 μ΄λ²ˆμ—λŠ” μ“°λ ˆλ“œ 두 개λ₯Ό λ™μ‹œμ— λŒλ €λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

    public static void main(String[] args) {
        Runnable task = new Task();

        Thread thread1 = new Thread(task);
        thread1.start();
        System.out.println("thread 1 executed.");

        Thread thread2 = new Thread(task);
        thread2.start();
        System.out.println("thread 2 executed.");

        System.out.println("μ“°λ ˆλ“œ 콜 μ’…λ£Œ");
    }

    static class Task implements Runnable {

        int num = 0;

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ", num=" + num++);
            }
        }
    }

 

쀑간쀑간에 μ–΄λ–»κ²Œ 싀행이 λ˜λŠ”μ§€ 확인을 ν•˜κΈ° μœ„ν•˜μ—¬ System.out.println으둜 λ‘œκΉ…μ„ ν•΄λ΄€μŠ΅λ‹ˆλ‹€.

thread 1 executed.
Thread-0, num=0
Thread-0, num=1
Thread-0, num=2
Thread-0, num=3
Thread-0, num=4
Thread-0, num=5
thread 2 executed.
μ“°λ ˆλ“œ 콜 μ’…λ£Œ
Thread-0, num=6
Thread-1, num=7
Thread-1, num=9
Thread-0, num=8
Thread-1, num=10
Thread-0, num=11
Thread-1, num=12
Thread-1, num=13
Thread-1, num=14
Thread-1, num=16
Thread-1, num=17
Thread-1, num=18
Thread-1, num=19
Thread-0, num=15

BUILD SUCCESSFUL in 0s
2 actionable tasks: 1 executed, 1 up-to-date

 

μœ„ 좜λ ₯ κ²°κ³Όλ₯Ό 보면 μ“°λ ˆλ“œκ°€ λ¨Όμ € μ‹œμž‘λ˜μ§€λ§Œ "task1 executed"κ°€ λ¨Όμ € 좜λ ₯이 λ˜μ—ˆκ³  κ·Έ 뒀에 첫 번째 μ“°λ ˆλ“œμΈ Thread-0이 좜λ ₯을 μ‹œμž‘ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 그리고 쀑간에 "task2 executed"κ°€ 좜λ ₯된 κ±Έ λ³΄λ‹ˆ 두 번째 μ“°λ ˆλ“œκ°€ 싀행이 된 것을 μ•Œ 수 있죠. 두 번째 μ“°λ ˆλ“œλŠ” Thread-1 이며 첫 번째 μ“°λ ˆλ“œκ°€ 일을 마치기 전에 μ‹œμž‘λ˜μ–΄ "Thread-1, num=0"을 좜λ ₯ν•œ 것을 확인 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ΄λ ‡κ²Œ ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€(main ν”„λ‘œμ„ΈμŠ€) μ•ˆμ—μ„œ μ—¬λŸ¬ 개의 μž‘μ—…μ„ λ™μ‹œμ— λ‚˜λˆ„μ–΄ μ‹€ν–‰ ν•  수 μžˆλ„λ‘ ν•΄μ£ΌλŠ” 것이 λ°”λ‘œ 이 μ“°λ ˆλ“œμž…λ‹ˆλ‹€.

μ“°λ ˆλ“œλ‘œ μž‘μ—…μ„ ν•˜κ²Œ 되면 μ•„λž˜μ™€ 같은 νŠΉμ§•λ“€μ΄ μžˆμŠ΅λ‹ˆλ‹€.

  • μ—¬λŸ¬ μž‘μ—…μ„ λ™μ‹œμ— μ²˜λ¦¬ν•  수 μžˆμ–΄ μž‘μ—…μ„ μ™„λ£Œν•˜λŠ”λ° ν•„μš”ν•œ 총 μ†Œμš” μ‹œκ°„μ΄ 쀄어든닀. (λ©”μΈμ“°λ ˆλ“œ 1개둜 μž‘μ—…ν–ˆμ„ λ•Œμ™€ λΉ„κ΅ν–ˆμ„ λ•Œ)
  • λ¨Όμ € μ‹œμž‘ν•œ μ“°λ ˆλ“œκ°€ 항상 λ¨Όμ € 일을 λλ‚΄μ§€λŠ” μ•ŠλŠ”λ‹€. λ”°λΌμ„œ, μž‘μ—…μ˜ μˆœμ„œκ°€ μ€‘μš”ν•  λ•Œμ—λŠ” μ“°λ ˆλ“œλ‘œ λ‚˜λˆ„μ–΄ μ²˜λ¦¬ν•˜λ©΄ μ•ˆλœλ‹€.

 

자, 이제 μ‹±κΈ€ μ“°λ ˆλ“œλ‘œ 10μ‹œκ°„ 걸릴 일을 100개의 μ“°λ ˆλ“œλ₯Ό λŒλ €μ„œ 10λΆ„λ§Œμ— 끝내고 μ‹ΆμŠ΅λ‹ˆλ‹€. μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒμš”?

μ“°λ ˆλ“œλ₯Ό 100개λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•˜λŠ”λ° μ•„λž˜μ²˜λŸΌ λ¬΄μ‹ν•˜κ²Œ λ§Œλ“€μ–΄μ•Ό ν• κΉŒμš”?

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        Thread thread3 = new Thread(task);
        Thread thread4 = new Thread(task);
        Thread thread5 = new Thread(task);
        .
        .
        .

 

μ•„λ‹™λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•΄μ•Όλ˜λ©΄ μ“°λ ˆλ“œ λ§Œλ“€λ‹€κ°€ ν‡΄κ·Όν•΄μ•Όλ©λ‹ˆλ‹€.

μžλ°”μ—μ„œλŠ” java.util.concurrentνŒ¨ν‚€μ§€μ— λ™μ‹œμž‘μ—…μ„ μœ„ν•΄ μœ μš©ν•œ ν΄λž˜μŠ€λ“€μ„ λͺ¨μ•„λ†¨λŠ”λ° 그쀑에 ThreadPoolExecutorλΌλŠ” 녀석이 μžˆμŠ΅λ‹ˆλ‹€. 이 녀석을 μ‚¬μš©ν•˜λ©΄ 두 μ„Έ 쀄이면 100개의 μ“°λ ˆλ“œλ₯Ό 돌릴 수 μžˆλŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•  수 있죠.

ThreadPoolExecutor에 λŒ€ν•΄ μ„€λͺ…ν•˜λ €λ©΄ ν¬μŠ€νŠΈκ°€ κΈΈμ–΄μ§€λ‹ˆ ThreadPoolExecutorλ₯Ό μ΄μš©ν•œ λ©€ν‹°μ“°λ ˆλ“œ κ΅¬ν˜„μ„œ 계속 μ΄μ–΄κ°€κ² μŠ΅λ‹ˆλ‹€.