241018 - TIL Java Thread 개념정리 (join, yield, synchronized)

J_log·2024년 10월 18일
0

Java 스터디 정리

목록 보기
5/5

join()

메인 쓰레드가 종료되면 실행 중인 쓰레드가 있어도 작업을 종료시켜 버린다 하지만 join()을 사용하면
정해진 시간 동안 지정한 쓰레드가 작업하는 것을 기다렸다가 종료한다.

  • 시간을 지정하지 않았을 때는 지정한 쓰레드의 작업이 끝날 때까지 기다린다.

join() 사용법

Thread thread = new Thread(task, "thread");

thread.start();

try {
    thread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
  • Thread.sleep(ms); -> 밀리세컨드 단위로 설정된다.
  • interrupt()를 만나면 기다리는 것을 멈추기 때문에 예외처리를 해주어야 합니다.

예제 코드

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                Thread.sleep(5000); // 5초
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        Thread thread = new Thread(task, "thread");

        thread.start();

        long start = System.currentTimeMillis();

        try {
            thread.join();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("소요시간 = " + (System.currentTimeMillis() - start));
    }
}

메인 쓰레드가 종료되면 모든 쓰레드를 하는데 join()으로 인해 thread의 소요시간인 5초 동안 main 메인 쓰레드가 기다렸다가 종료됩니다.


yield()

실행 중인 쓰레드가 interrupt되면 나은 자원을 다음 쓰레드에게 양보하고 자신은 실행 대기 상태가 된다.

yield() 사용법

Runnable task = () -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                Thread.yield();
            }
        };

예제 코드

public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                Thread.yield();
            }
        };

        Thread thread1 = new Thread(task, "thread1");
        Thread thread2 = new Thread(task, "thread2");

        thread1.start();
        thread2.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread1.interrupt();

    }
}

thread1과 2가 1초에 한 번씩 출력되다가 5초뒤에 thread1에서 interruptedException이 발생하면서 Thread.yield();를 호출한다 thread1은 실행 대기 상태로 변경되고서 남은 자원은 thread2에게 양보한다.


synchronized

멀티 쓰레드의 경우 여러 쓰레드가 한 프로세스의 자원을 공유하는데 이 때문에 서로에게 영향을 줄 수 있다. 이를 방지하기 위해 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 들어오지 못하도록 막는 것을 Synchronization (동기화) 라고 한다.

synchronized를 사용한 동기화

  • 실행할 메서드 또는 실행할 코드 묶음 앞에 synchronized를 사용해 임계 영역을 지정하고 다른 쓰레드의 침법을 막을 수 있다. (Lock)

임계 영역 지정

  1. 메서드 전체를 임계 영역으로 지정하기
public synchronized void asyncSum() {
	  ...침범을 막아야하는 코드...
}
  1. 특정 부분을 임계 영역으로 지정하기
synchronized(해당 객체의 참조변수) {
		...침범을 막아야하는 코드...
}

synchronized 코드 비교

  • 없는 경우
public class Main {
    public static void main(String[] args) {
        AppleStore appleStore = new AppleStore();

        Runnable task = () -> {
            while (appleStore.getStoredApple() > 0) {
                appleStore.eatApple();
                System.out.println("남은 사과의 수 = " + appleStore.getStoredApple());
            }

        };

        for (int i = 0; i < 3; i++) {
            new Thread(task).start();
        }
    }
}

class AppleStore {
    private int storedApple = 10;

    public int getStoredApple() {
        return storedApple;
    }

    public void eatApple() {
        if (storedApple > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            storedApple -= 1;
        }
    }
}

남아 있는 수에 마이너스가 출력되는 모습을 볼 수 있다.

  • 있는 경우
public class Main {
    public static void main(String[] args) {
        AppleStore appleStore = new AppleStore();

        Runnable task = () -> {
            while (appleStore.getStoredApple() > 0) {
                appleStore.eatApple();
                System.out.println("남은 사과의 수 = " + appleStore.getStoredApple());
            }

        };

        for (int i = 0; i < 3; i++) {
            new Thread(task).start();
        }
    }
}

class AppleStore {
    private int storedApple = 10;

    public int getStoredApple() {
        return storedApple;
    }

    public void eatApple() {
        synchronized (this) {
            if(storedApple > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                storedApple -= 1;
            }
        }
    }
}

정상적인 숫자가 출력된다.

post-custom-banner

0개의 댓글