-
join, yield, synchronizedJava/Java 문법 종합반 강의 정리 2024. 1. 3. 17:42
join() : 정해진 시간동안 지정한 쓰레드가 작업하는 것을 기다린다.
- 시간을 지정하지 않았을 때는 지정한 쓰레드의 작업이 끝날 때 까지 기다린다.
join 사용방법
Thread thread = new Thread(task, "thread"); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); }
- Thread.sleep(ms), ms(밀리초) 단위로 설정된다.
- 예외처리를 해야한다.
- interrupt() 를 만나면 기다리는 것을 멈추기 때문에 InterruptedException 이 발생할 수 있다.
- 시간이 지정하지 않았기 때문에 thread가 작업을 끝낼 때 까지 main 쓰레드는 기다리게 된다.
쓰레드가 일을 마칠 때 까지 다른 작업을 멈춘다거나,
해당 쓰레드가 작업을 마치는데 걸리는 시간을 측정하는 등의 사용법이 존재한다.
자세한 코드는 별거 없어서 생략했다.
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 과 thread2 가 같이 1초에 한번씩 출력되다가 5초뒤에 thread1에서 intterruptException 이 발생하면서
Thread.yield() 이 실행되어 thread1 은 실행대기 상태로 변경되면서 남은 시간은 thread2 에게 리소스가 양보된다.
synchronized
- 멀티 쓰레드의 경우 여러 쓰레드가 한 프로세스의 자원을 공유해서 작업하기 때문에 서로에게 영향을 줄 수 있다.
이로 인해서 장애나 버그가 발생할 수 있다.
- 이러한 일을 방지하기 위해 한 쓰레드가 작업중인 작업을 다른 쓰레드가 침범하지 못하도록 막는 것을
'쓰레드 동기화(Synchronization)'라고 한다.
- 동기화를 하려면 다른 쓰레드의 침범을 막아야하는 코드들을 '임계영역'으로 설정하면 된다.
- 임계영역에는 Lock을 가진 단 하나의 쓰레드만 출입이 가능하다.
- 즉, 임계영역은 한번에 한 쓰레드만 사용이 가능하다.
synchronized 를 사용한 동기화
- 실행할 메서드 또는 실행할 코드 묶음 앞에 synchronized 를 붙여서 임계영역을 지정하여 다른 쓰레드의 침범을
막을 수 있다. (침범을 막다. = Lock을 걸다.)
임계영역 지정 방법 두가지
1. 메서드 전체를 임계영역으로 지정한다.
public synchronized void asyncSum() { ...침범을 막아야하는 코드... }
2. 특정 영역을 임계영역으로 지정한다.
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() { synchronized (this) { if(storedApple > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } storedApple -= 1; // 쓰레드 마다 사과를 한개씩 감소시킨다. } } } }
만약 synchronized 를 걸지 않았다면, if문의 조건인 storedApple > 0 이 아님에도 불구하고 while문의 로직을 실행한다.
하지만 synchronized 를 건 덕에, eatApple 메서드 안에서 임계영역(if문)을 수행할 수 있는 것은 하나의 쓰레드로 제한된다.
사과가 0개가 된 이후엔 조건문을 통과하지 못한 나머지 쓰레드들 또한 0개의 사과를 출력한다.
git 사용에서도 그랬듯이, 충돌 상황이라는 것은 많이 겪어보면서 배우는 것이 가장 빠르다.
때가 되면 이 내용을 떠올리며 최대한 많이 부딪쳐보자.
'Java > Java 문법 종합반 강의 정리' 카테고리의 다른 글
Lock, Conditon (6) 2024.01.03 wait(), notify() (1) 2024.01.03 쓰레드의 상태와 제어 (2) 2024.01.03 데몬 쓰레드, 쓰레드 우선순위, 쓰레드 그룹 (0) 2024.01.03 쓰레드 사용법 (1) 2024.01.03