권한을 얻을 때까지 확인한다.
Busy Waiting
주기적인 실행, 특정 시간 이후 실행 등의 작업을 하고 싶을 때는
Timer, TimerTask 같은 클래스를 활용도 고려해보자~
(java.util 에서 제공한다.)
동기화 문제와 바쁜 대기를 해결하여 구현되어 있다.
최근 Web으로 interactive하게 편집할 수 있는 편집기를 프로젝트로 진행하고 있다.
미리캔버스, Figma 등과 같은 느낌이라고 보면 될 것이다.
편집기에는 임시저장이 필수인데, 이를 구현하다보니
while을 사용한 무한루프로 특정 시간(10분)이 지날때마다 임시저장을 하도록 구현했다.
Thread threadA = new Thread(()->{
while(flag){
if(LocalDateTime.now().getSecond()%5==0){
System.out.println("저장!" + LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
Thread.sleep(1000)으로 1초씩 기다리게 했지만, 결국 5초에 한 번만 발생하는 일이 5번발생하게 된다.
-> IDE에서 바쁜 대기 상태일 수도 있다고 경고를 띄워준다.
이러한 상황을 바쁜 대기 상태라 한다.
바쁜대기
원하는 자원을 얻기 위해 기다리는 것이 아니라 권한을 얻을 때까지 확인하는 것을 의미합니다.
BusyWaiting기법이 필요한 경우
- 자원 얻는데 소모되는 시간이 적을 경우
- Context Switching 비용보다 성능적으로 더 우수한 상황인 경우
- 공유 자원에 대한 권한 획득이 아주 빠른 시간 내에 이루어질 수 있다는 확신이 있는 상황 또는 뮤텍스나 세마포어 등의 동기화 객체등을 이용하기에는 그 오버헤드가 큰 상황에서 간단히 쓸 수 있다.(출처 : 위키)
단점 : CPU가 낭비된다
이 단점을 해결할 수 있는 방법을 알아봅시다.
- sleep : 시간 예측이 가능한 경우~
- wait, notify : thread를 재우고, 깨우고
- join메서드 : 실행 순서를 보장한다.
해결책 - sleep : 시간 예측이 가능한 경우~
- 시간 예측이 가능한 경우 sleep()을 활용하여, 현재 thread가 불필요한 동작을 하지 않도록 blocked상태로 만들어준다.
Thread threadA = new Thread(()->{
while(flag){
System.out.println("저장!" + LocalDateTime.now());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
매 5초마다 실행하는 프로그램은 위와 같이도 설정이 가능하다.
해결책 - wait, notify : thread를 재우고, 깨우고
wait()은 java의 최상위 객체 타입인 Object에 포함되어 있는 method이다.
- wait : 현재 실행 중인 Thread를 일시정지(Blocked) 상태로 변경한다. wait()을 호출한 thread를 중지시킴
-> 아래 코드에서 waitThread.wait()을 호출한 mainThread가 정지 상태로 변경된다.
-> wait 메서드는 동기화 블록 내에서만 사용이 가능하다. - notify : wait하고 있는 thread를 깨운다.
public static void main(String[] args) {
WaitThread waitThread = new WaitThread();
waitThread.start(); //스레드 실행
// 동기화 블록
synchronized (waitThread) {
System.out.println("동기화블록 진입");
try {
// 동기화 시켜놓고 waitThread wait호출 -> mainThread가 중지됨
//-> wait메서드를 호출한 객체가 실행되는 thread를 중지시키는 것(=mainThread)
System.out.println("wait~!!");
waitThread.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("종료");
}
}
- 10초 동안 쉬고, notify가 호출되면서 mainThread가 깨어난다.
public static class WaitThread extends Thread {
@Override
public void run() {
synchronized (this) {
try {
System.out.println("sleep 10초");
Thread.sleep(10000);
System.out.println("notify!");
this.notify();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
main코드 실행 console
동기화블록 진입
wait~!!
sleep 10
notify!
종료
공부하는 김에 join도 알아보자
Thread - join메서드 : 실행 순서를 보장한다.
다음 코드를 실행시키면 이미 실행시킨 thread가 실행 중인 상태에서 MainThread가 먼저 종료된 것을 확인할 수 있다.
join은 join메서드를 호출한(main Thread)가 join대상 thread가 종료 될 때까지 기다린다.
public static void main(String[] args) {
for(int i=0; i<5; i++){
Thread thread = new JoinThread();
thread.setName("join_"+i);
thread.start();
}
System.out.println("MainThread End");
}
MainThread End
join_1 is Processing : 2023-10-20T21:43:56.075742600
join_3 is Processing : 2023-10-20T21:43:56.075742600
join_2 is Processing : 2023-10-20T21:43:56.075742600
join_0 is Processing : 2023-10-20T21:43:56.075742600
join_4 is Processing : 2023-10-20T21:43:56.075742600
join_1 is Terminated : 2023-10-20T21:43:57.085685400
join_2 is Terminated : 2023-10-20T21:43:57.085685400
join_3 is Terminated : 2023-10-20T21:43:57.085685400
join_0 is Terminated : 2023-10-20T21:43:57.099547700
join_4 is Terminated : 2023-10-20T21:43:57.099547700
위 코드를 실행하면 아래와 같은 흐름으로 실행된다.
start() 메서드를 통해 실행된 Thread 사이에는 순서를 보장할 수 없다.
뭐가 먼저 실행될 지 모른다는 얘기...
join메서드가 포함된 실행코드 : 실행 순서를 보장할 수 있다.
public static void main(String[] args) {
for(int i=0; i<5; i++){
Thread thread = new JoinThread();
thread.setName("join_"+i);
thread.start();
try {
thread.join();
}catch (InterruptedException interruptedException){
interruptedException.printStackTrace();
}
}
System.out.println("MainThread End");
}
join이 있을 때 : 위 코드의 실행 순서는 다음과 같다.
추가 공부 필요
- 뮤텍스, 세마포어
- lock
- thread 세부
- Timer, TimerTask
'개념' 카테고리의 다른 글
[용어] 웹서비스 - 프론트/백 오피스, 프론트/백엔드 (0) | 2024.01.13 |
---|---|
[개념] Proxy 프록시 서버 : 대신하는 것. (0) | 2022.12.27 |
댓글