14_자바(JAVA)_프로그램_Thread_Lifecycle_sleep_notify_wait_yield_join_synchronize_priority
안녕하세요 MrChooCAI입니다~@#@#%
이번 포스트에서는 쓰레드에 대해서 자세하게 알아 보도록 하겠습니다~
실습 코드는 github에서 확인해 주시면 되겠습니다.
Github: https://github.com/Chooyoungjun/javastudy/tree/main/15_00_Thread_Lifecycle
[설명 순서]
1.
프로세스와 쓰레드의 정의
2.
쓰레드의 생성
3.
쓰레드의 우선 순위(priority)
4. 쓰레드의 라이프 사이클(sleep, yield, join)
5.
쓰레드의 동기화(synchronize)
6.
wait & notify
쓰레드(thread)
1.
프로세스와 쓰레드의 정의
프로세스란? 간단히 말해서 '실행 중인 프로그램'을 의미합니다.
프로그램을 실행하면 OS(window, linux, max …)로부터 실행에 필요한 자원(runtime 메모리)을 할당 받아 프로세스가 되어서 프로그램을 실행시키는
주체가 됩니다. 프로세스는 프로그램을 수행하는 데 필요한 데이터와 메모리 등의 자원 그리고 쓰레드로
구성되어 있으며, 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것이 바로 쓰레드 입니다. 그래서 모든 프로세스에는 최소한 하나
이상의 쓰레드가 존재한다고 말할 수 있습니다. 둘 이상의 쓰레드를 가진 프로세스를 멀티쓰레드 프로세스라고
합니다. 멀티쓰레딩은 하나의 프로세스 내에서 여러 쓰레드가 동시에 작업을 수행하는 것이 가능합니다. 실재로는 한 개의 CPU가 한 번에 단 한가지 작업만 수행할 수
있기 때문에 아주 짧은 시간 동안 여러 작업을 번갈아 가며 수행함으로써 동시에 여러 작업이 수행되는 것처럼 보이게 하는 것입니다.
[용어정리]
[프로세스가 실행되는 방식]
-
시간 분할 방식
모든
프로세스에게 동일한 시간을 할당하고 골고루 실행되는 방식
-
선점형 방식
각각의
프로세스에게 우선 순위를 부여하고 우선순위가 높은 순으로 실행되는 방식
[JVM이 쓰레드 처리시 하는 일]
-
쓰레드가 몇 개 존재하는지 확인
-
쓰레드가 실행되는 프로그램 코드의 메모리 위치 확인
-
현재 쓰레드의 상태 확인
-
쓰레드의 우선순위 확인
JVM은 저희가 처음
포스트에서 언급했었는데 이렇게 프로그램을 오류 없이 빠르게 필요한 프로세스를 실행할 수 있도록 이런 많은 일을 해 주고 있습니다. JVM이 처리해 주는 작업은 자동으로 되는 작업입니다. 그러면 개발자가
쓰레드에 처리하는 일에 대해서 알아보도록 하겠습니다.
[개발자가 쓰레드
처리시 하는 일]
-
자바 쓰레드로 작동할 작업이 무엇인지 코드로 작성
-
쓰레드 코드가 실행할 수 있도록 JVM한테 요청
개발자가 해야 할 일은 생각 보다 간단하죠? 그래도 저희는 뒤에서 어떤 일들이 일어나는지 알아가기 위해서 프로세스와 쓰레드에 대해서 알아보았습니다. 이제부터는 쓰레드의 생성에 대해서 알아보도록 하겠습니다.
2.
쓰레드의 생성
[쓰레드 작업 코드 작성 방법]
쓰레드
작업 코드 작성 방법은 상속하는 방법과 인터페이스 구현하는 방법 2가지가 있습니다. 그래서 2가지에 대해서 다 정리해 보도록 하겠습니다.
-
Thread 클래스 상속
[구현]
class thread1 extends Thread{
public
void run(){ //run method 오버라이딩
//작업할 내용!! } }
[실행 방법]
실행시킬
클래스 안에서 하기 코드를 넣으시면 되겠습니다.
thread1 th1= new thread1();
th1.start();
위와 같이 실행시켜 주시면 위 run코드에서
정의 했던 명령문이 실행 되는 것을 알 수 있습니다.
-
Runnable 인터페이스 구현
[구현]
class thread2 implements Runnable{
public
void run(){ //run method 오버라이딩
//작업할 내용!! } }
[실행 방법]
실행시킬
클래스 안에서 하기 코드를 넣으시면 되겠습니다.
thread2 th2 = new thread2();
Thread t = new Thread(th2);
t.start();
위와 같이 실행시켜 주시면 위 run코드에서
정의했던 명령문이 실행 되는 것을 알 수 있습니다.
[실습]
3가지의 쓰레드를 실행시켜 보겠습니다.
1번 main Thread, 2번 Thread 상속
Thread, 3번 Runnable 인터페이스 상속 쓰레드
이렇게 3가지에 대해서 실습을 진행해 보도록 하겠습니다.
3가지 Thread가 한 개의
프로세스에서 실행 중입니다. 보시면 순서에 상관없이 동시에 작업 되는 것처럼 보입니다.
3.
쓰레드의 우선 순위
기본으로
쓰레드는 시분할 방식으로 CPU의 시간을 분배하여 실행하지만, 사용자가
직접 쓰레드의 우선순위를 지정해서 특정 쓰레드에 더 많은 실행시간 부여가 가능 합니다.
[쓰레드 우선순위 메서드]
-
우선순위는 1부터 10까지 부여가능
-
설정하지 않을 경우 기본적으로 5가 부여됨
[실습]
먼저 시작한 쓰레드 일수록 우선순위를 낮게 지정하여 실행
위의 실습코드에서 보여주듯이 쓰레드 1을 제일 먼저 시작 했지만 priority가 1로 되어 있어서, 종료가 제일 마지막에 되는 것을 확인 할 수 있습니다. 그리고 위에서 설명드리지 않았지만 실행하는 쓰레드의 name을 반환하는
코드는 Thread.currentThread().getName()인 것을 참고해 주시기를 부탁 드리겠습니다.
4.
쓰레드의 라이프 사이클
일반적인
쓰레드의 실행 구조는 NEW à RUNNABLE à ScheduleràRUNNING àTERMINATED이렇게 4가지 구조를 가지고 있습니다 그런데 여러
개의 쓰레드가 실행되는 경우에는 무슨 쓰레드부터 실행 해야 할까요? 그리고 쓰레드를 일정한 시간마다
실행 시킬 방법을 없을까요? 여기에 대해서 알아보기 위해서 3가지
Method(sleep, yield, join)에 대해서 알아보도록 하겠습니다.
[쓰레드의 라이프 사이클 그래프입니다.]
전체적인 쓰레드의 라이프 사이클을 좀 더 간단하게 표로 그려 보았습니다. 위와 같은 흐름을 가지고 있습니다. 이번에 저희는 Timed waiting과 blocked에 대해서 알아보고 뒤에서는 synchronized에 대해서 알아보도록 하겠습니다.
[sleep]
쓰레드를
지정한 시간 동안 block 상태로 만드는 메서드 입니다.
[sleep 실습]
Thread.sleep()을 활용해서 카운트 다운하는 프로그램을 만들어 알아가
보도록 하겠습니다.
위와
같이 ms기준으로 초를 카운트할 수 있습니다. 1000ms는
1초로 환산됩니다. 그래서 위의 코드를 실행시키면 1초마다 숫자를 print하는 것을 알 수 있습니다. 위에서 interruptedException을 실행 시키는데 그
이유는 sleep자체가 흐름을 방해하기 때문에 interruptedException을
받아 주어야 합니다.
[yield]
자신에게
할당된 시간을 양보하는 메서드
쓰레드가
작업을 수행하던 중 yeild를 만나면, 자신에게 할당된
시간을 다음 차례 쓰레드에게 양도합니다.
[yield 실습]
Thread.yeild()를 활용해서 @와 #을 프린트하는 프로그램을 통해서 어떻게 동작하는지 확인해 보도록 하겠습니다.
위와
같이 @이가 한번 실행되었을 때 다른 쓰레드가 들어오면 양보하는 것을 확인할 수 있습니다. 그래서 #이 늦게 시작되었지만 빨리 종료되는 것을 확인할 수 있습니다.
[join]
특정한
쓰레드가 작업을 먼저 수행할 때 사용
원하는대로
실행 순서를 설정 할 수 있도록 돕는 메서드 입니다.
[join 실습]
3개의 쓰레드를 실행시켜서 join을 활용해서 다른 쓰레드를 Block시켜서 하나씩 실행 시키는 실습을 진행해 보도록 하겠습니다.
위의
실습코드처럼 join메서드를 활용해서 하나씩 실행시키고 있는 것을 알 수 있습니다.
5.
쓰레드의 동기화(synchronized)
멀티
쓰레드로 작업 시, 쓰레드 간 작업이 서로 간섭이 되지 않도록 하는 방법을 쓰레드의 동기화라고 합니다.
[문법]
2가지 사용 방법이 있습니다.
첫번째는
synchronize가 필요한 곳에 중괄호{}를 활용해서
적용 하는 방법 입니다.
synchronized(this){
//필요한 명령 }
두번째 방법은 메서드를 정의할 때 synchronized키워드를
추가 하는 방법 입니다.
public synchronized void setMoney(){
//필요한 명령 }
위의 문법을 바탕으로 실습을 진행해 보도록 하겠습니다.
[문제 정의]
멀티
쓰레드를 실해 시킬 경우 동시에 같은 자원을 처리한다면 실제 처리해야 하는 작업보다 더 많은 작업이 발생할 수도 있습니다. 예를 들어 커플 통장을 사용하는데 그런 일이 없겠지만 1000ms단위로
같은 시간에 출금을 하면 한 번만 출금 처리 되는 경우가 발생할 수도 있습니다. 이렇게 되면 완전 좋죠~ 은행은 망하고요…
이런
문제를 해결 하는 것이 쓰레드의 동기화 방법 입니다.
[실습_문제 발견]
한정된
티켓 수량을 예약하는 프로그램을 만들어 보도록 하겠습니다.
위의
실습 코드에서 티켓 개수가 -까지 가는 것을 확인할 수 있습니다. 그
이유는 ticketNumber가 -가 반영되기전에 이미 3개다 실행이 되었기 때문에 이런 현상이 발생합니다. 그래서 이런 문제를
해결하기 위해서 동기화가 필요합니다.
[실습_문제 해결]
티켓팅
되는 코드를 synchronized로 감싸보겠습니다.
위의
실습 코드와 같이 synchronized로 감싸면 티켓팅이 0이하로
되지 않는 것을 확인 할 수 있습니다.
6.
wait & notify
한 쓰레드가 객체에 lock을 걸고 어떤 조건이 만족될
때 까지 기다려야하는 경우, 이 쓰레드를 그대로 놔두면 이 객체를 사용하려는 다른 쓰레드들은 lock이 풀릴 때까지 같이 기다려야하는 상황이 발생합니다. 이러한
비효율을 개선하기 위해서 wait()와 notify()를
사용합니다. 한 쓰레드가 lock을 걸고 오래 기다리는 대신에 wait()를 호출하여 다른 쓰레드에게 제어권을 넘겨주고 대기상태로 기다리다가 다른 쓰레드에 의해서 notify()가 호출되면 다시 실행상태가 되도록 하는 것을 말합니다.
[메서드]
위와
같이 4가지의 method를 제공합니다. 위의 메서드들을 어떻게 활용하는지 실습을 통해서 알아보도록 하겠습니다.
[실습]
어머니가
용돈을 붙여주지 않으면 아들은 돈을 사용할 수 없습니다. 그래서 아들은 용돈이 들어올 때까지 기다려야합니다. 그래서 getMoney메서드는 wait함수를
가지고 있어야 합니다. 그리고 setMoney메서드에서는
wait하고 있는 쓰레드를 깨우기 위해서 notify메서드를
실행 시켜야 합니다.
[Account class생성]
공동으로
공유해야 하는 클래스를 생성해야 합니다. getmoney와 setmoney에서는
synchronized로 생성하여야 하면 각각의 역할에 따라서 wait와
notify함수를 적용시켜야 합니다.
위의
코드와 같이 wait와 notify를 활용해서 용돈이 없을
경우에는 기다릴 수 있도록 코드를 작성 합니다.
[son, mom, main class 생성]
통장
잔고가 없으면 wait함수로 기다리게 만듭니다. 그리고 어머니가
용돈을 넣어주면 notify가 wait하고 있는 아들 클래스의
getmoney 메서드를 깨웁니다. 그래서 용돈을 넣고 난
다음 다시 실행되는 것을 확인할 수 있습니다.
긴 글을 읽어주셔서 감사합니다.
Comments
Post a Comment