일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- JavaScript
- java.lang 패키지
- nextInt
- Spring
- 반복문
- Programming
- StringTokenizer
- 해시
- 프로그래머스
- Computer Science
- softeer
- SQL
- MySQL
- 자바의 정석(기초편)
- 소프티어
- 백준
- programmers
- 티스토리챌린지
- Steve Jobs
- 영어원서
- 관계형 데이터베이스
- 코딩테스트
- BFS
- Java script
- 오블완
- thinking differently
- Java
- 알고리즘
- Spring Framework
- Python
- Today
- Total
도라에몽 개발자
쓰레드 (thread) 본문
프로세스와 쓰레드 (process & thread)
- 프로세스(process)
- 실행 중인 프로그램을 의미함.
- 자원(resource; 메모리, CPU 등)과 쓰레드로 구성되어 있음. - 쓰레드(thread)
- 프로세스 내에서 실제 작업을 수행하는 것을 의미함.
- 모든 프로세스는 최소한 하나의 쓰레드를 가지고 있음. ★
1) 싱글 쓰레드 프로세스
= 자원 + 쓰레드
→ 각 작업이 순차적으로 수행됨.
2) 멀티 쓰레드 프로세스
= 자원 + 쓰레드 + 쓰레드 + ... + 쓰레드
→ 여러 작업을 동시에(번갈아가면서) 수행함으로써 효율성이 높아지는 장점이 있음. (★ 'OS 스케쥴러에 따른 순서'로 실행됨 ★)
→ context switching으로 인한 시간 소요로 싱글 쓰레드 프로세스보다 진행시간이 다소 길다는 단점이 있음. (But, 장점 >> 단점)
프로세스는 공장과 같고, 쓰레드는 일꾼과 같다.
하나의 새로운 프로세스를 생성하는 것보다는, 하나의 새로운 쓰레드를 생성하는 것이 더 적은 비용이 든다.
(일꾼이 한 명밖에 안되는 공장을 다시 설립하는 것보다, 여러 명의 일꾼을 둔 공장 하나가 더 낫다는 의미로 볼 수 있다.)
멀티쓰레드의 장단점
- 장점
- CPU의 사용률을 향상시킴.
- 시스템 자원을 보다 효율적으로 사용할 수 있음.
- 사용자에 대한 응답성(responseness)이 향상됨.
- 작업이 분리되어 코드가 간결해짐.
Ex. 메신저 상에서 서로 텍스트로 대화를 하면서, 음성 대화도 나누고, 자유롭게 파일도 주고 받을 수 있음. (동시다발적 실행 가능) - 단점
- 동기화(synchronization)에 주의해야 함. (∵ 같은 프로세스 내에서 여러 자원(resource)을 공유함에 따른 부작용)
- 교착상태(dead-lock)가 발생하지 않도록 주의해야 함. (각 기능이 대치하는 상황으로, 기아 상태에 도래할 수 있어서 주의할 것)
- 각 쓰레드가 효율적으로 고르게 실행될 수 있도록 주의해야 함.
cf. 교착상태(dead-lock)란?
두 쓰레드가 하나의 자원을 점유한 상태에서, 서로 상대편이 점유한 자원을 사용하려고 대치하는 상태로 프로그램 진행이 멈춰있는 상태를 의미함.
쓰레드의 구현과 실행
쓰레드를 구현하는 방법
- Thread 클래스 상속
- 자바의 '단일 상속'이라는 특징으로 인해, thread 클래스를 상속 받으면 다른 클래스를 상속 받을 수 없다는 단점이 있음.
- ... extends ... - Runnable 인터페이스 구현
- 재사용성(reusability)이 높고, 코드의 일관성(consistency)을 유지할 수 있음.
- 쓰레드 클래스를 상속하는 것보다 비교적 객체지향적인 방법임에 따라, runnable 인터페이스를 구현하는 방법을 빈번하게 사용하고 있음.
- ... implements ...
// Thread 클래스 상속
class MyThread extends Thread {
public void run() { ... }
}
// Runnalbe 인터페이스 구현
class MyThread implements Runnable {
public void run() { ... }
}
쓰레드를 실행하는 방법
- start() 메서드
- 쓰레드를 생성한 후, start() 메서드를 호출해야 쓰레드가 작업을 시작함.
- OS 스케쥴러가 실행순서를 결정함. (즉, start() 메서드의 작성 순서와 상관 없이 출력 순서가 변할 수 있는 불확실성이 있음.) ★ - run() 메서드
- main 메서드에서 run() 메서드를 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아닌, 클래스에 선언된 메서드를 호출하는 것임.
- 반면, start() 메서드의 경우 새로운 쓰레드가 작업을 실행하는데 필요한 새로운 호출스택(call stack)을 생성한 후 run() 메서드를 호출하고 생성된 호출스택에서 run()이 호출되어 작업을 수행하게 됨.
→ 호출스택이 2개임에 따라, 독립적으로 각 기능 실행이 가능함. ★ - main 쓰레드
- main 메서드의 코드를 수행하는 쓰레드임.
- 종류(2가지): 사용자 쓰레드(main), 데몬 쓰레드(보조)
- 실행 중인 사용자 쓰레드가 하나도 없어야 프로그램이 완전히 종료됨. (즉, 하나의 쓰레드라도 실행되고 있으면 프로그램 종료되지 않음) ★★
쓰레드의 I/O 블락킹(blocking)
- 정의
- 쓰레드가 입출력(I/O; Input/Output) 처리를 위해 기다리는 것
Ex. 싱글 쓰레드인 경우에는 A, B 작업이 있을 때 A 작업의 처리가 끝나기 전까지는 B 작업이 진행될 수 없기 때문에 A 작업이 진행되지 못하고 대기하는 시간이 발생하게 됨. 이 때, A 작업이 진행되지 않고 멈춰있는 상황에서 B 작업도 시작될 수 없는 그 사이를 정지 상태인 'bocking'이라고 함.
vs 멀티 쓰레드인 경우, 사용자의 입력을 기다리는 동안 다른 쓰레드가 또 다른 작업을 처리하여 보다 효율적인 CPU 사용이 가능함.
쓰레드의 우선순위(priority of thread)
- 정의
- 작업의 중요도에 따라 쓰레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업시간을 갖도록 함.
- 우선순위가 높을수록 작업시간을 더 많이 부여하게 되어, 더 우선적으로 실행되도록 함.
But, 주어진 작업을 일찍 끝낼 확률을 높여주는 것이지, 무조건 일찍 끝내는 것을 보장해주지는 않음.
void setPriority(int newPriority);
int getPriority();
public static final int MAX_PRIORITY = 10; // 최대
public static final int MIN_PRIORITY = 1; // 최소
public static final int NORM_PRIORITY = 5; // 보통(기본값이 5임)
쓰레드 그룹
- 정의
- 서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것
- 모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 함.
- 쓰레드 그룹을 지정하지 않은 경우, 기본적으로 'main 쓰레드 그룹'에 속하게 됨.
- 자신을 생성한 쓰레드(부모 쓰레드)의 그룹과 우선순위를 상속 받게 됨.
데몬 쓰레드(daemon thread)
- 정의
- 일반 쓰레드(non-daemon thread)의 작업을 돕는 '보조적인 역할'을 수행함.
- 일반 쓰레드가 모두 종료되면 자동 종료됨.
- 가비지 컬렉터, 자동 저장, 화면 자동 갱신 등의 기능 실행 시에 사용됨. - 활용 방법
- 무한 루프와 조건문을 이용하여 실행 후 대기하다가, 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 함. (while문, if문)
▼ setDaemon(boolean on)의 경우, 반드시 start() 호출하기 전에 실행되어야 함.
boolean isDaemon(); // 쓰레드가 데몬 쓰레드인지 확인함.
void setDaemon(boolean on); // 쓰레드를 데몬 쓰레드 or 사용자 쓰레드로 변경함.
// 매개변수 on이 true면 데몬 쓰레드이고, false면 사용자 쓰레드임.
쓰레드의 상태
1) NEW: 쓰레드 생성되고, 아직은 start() 호출되지 않은 상태
2) RUNNABLE: 실행 중이거나, 실행이 가능한 상태
3) BLOCKED: 동기화블럭에 의해 일시정지 상태(lock 풀릴 때까지 대기해야 함)
4) WAITING, TIMED_WAITING: 일시정지 상태로, 쓰레드 작업 종료되지 않음.
5) TERMINATED: 쓰레드 작업 종료
쓰레드의 상태를 변화시키는 메서드
1) start(): 실행대기열에 저장되어 차례가 될 때까지 대기
2) yield(): 다시 실행대기 상태로 변하고, 다음 차례의 쓰레드가 실행상태가 됨.
3) suspend(), sleep(), wait(), join(), I/O block: 일시정지 상태로 변환시킴.
4) time-out, resume(), notify(), iterrupt(): 지정된 일시정지 시간이 다 된(time-out) 경우나, 해당 메서드들이 호출되면 일시정지 상태를 벗어나 다시 실행대기열에 저장되어 자기 차례를 기다리게 됨.
5) stop(): 실행 종료되어 쓰레드 소멸됨.
쓰레드의 실행제어
- sleep
- static void sleep(long millis): 지정된 시간(천분의 일초 단위)동안 쓰레드를 '일시정지'시킴. 지정된 시간이 지난 후 자동적으로 다시 실행 대기상태가 됨. (ex. 5000 = 5초) - join
- void join(), void join(long millis): 다른 쓰레드의 작업이 끝날 때까지 기다리는 것을 의미함. - interrupt
- void interrupt(): sleep()이나 join()에 의해 일시정지 상태인 쓰레드를 깨워서 실행 대기상태로 만드는 것을 의미함. - stop
- void stop(): 쓰레드를 즉시 종료시킴. - suspend
- void suspend(): 쓰레드를 일시정지시킴. - resume
- void resume(): 일시정지 상태에 있는 쓰레드를 실행 재개시킴. (실행 대기상태로 만듬) - yield
- static void yield(): 실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보하고, 자신은 실행 대기상태가 됨.
▶ static : 남을 중단시킬 수는 없어서 자기 자신을 일시정지 시켜서 실행 대기상태로 변환하는 메서드에 붙여줌. ★★★
Sleep()
- 정의
- 현재 쓰레드를 지정된 시간동안 멈추게(잠들게) 하는 것 - 형태
- static void sleep(long millis) → 멈추는 시간은 천분의 일(10^-3)초 단위로 작성하면 됨.
- static void sleep(long millis, int nanos) → 멈추는 10^-3초 + 10^-9초 단위로 작성하면 됨. - 특징
- 다시 실행 재개 시키기(깨우기) 위해서는, 항상 예외처리 해주어야 함. (InterruptedException이 발생하면 깨어남)
▼ 미리 깨우는 메서드를 만들어두면, 간결하게 깨우는 메서드 호출하여 실행 재개시킬 수 있음.
try {
Thread.sleep(1, 500000);
} catch(InterruptedException e) {}
// 위와 같은 try-catch문을 다음과 같은 delay() 메서드로 미리 선언해둘 것
void delay(long millis) {
try {
Thread.sleep(1, 500000);
} catch(InterruptedException e) {}
}
// 결과적으로 try-catch문 대신, delay(15); 만을 호출하면 쓰레드를 0.015초 동안 멈추게 할 수 있음.
interrupt()
- 정의
- 대기상태(WAITING)인 쓰레드를 실행대기 상태(RUNNABLE)로 만드는 것
Ex. sleep(), join(), wait() 등의 메서드로 인해 대기 상태에 있다가, interrupt() 메서드로 인해 깨어나 실행 대기상태로 변환됨. - 형태
- void interrupt(): 쓰레드의 interrupted 상태를 false에서 true로 변경함.
- boolean isInterrupted(): 쓰레드의 interrupted 상태를 반환함.
- static boolean interrupted(): 현재 쓰레드의 interrupted 상태를 알려주고, false로 초기화함.
suspend(), resume(), stop()
▶ deplicated method !! (∵ dead-lock; 교착상태 발생 가능성 높음)
- 정의
- 쓰레드의 실행을 일시정지, 재개, 완전정지 시킴. - 형태
- void suspend(): 쓰레드를 일시정지(waiting) 시킴.
- void resume(): suspend()에 의해 일시정지된 쓰레드를 실행 대기상태로 만듬.
- void stop(): 쓰레드를 즉시 종료시킴.
cf. suspend - resume 의 경우, 반대의 기능을 하는 한 셋트로 볼 수 있음.
join()
- 정의
- 지정된 시간동안 특정 쓰레드가 작업하는 것을 기다림.
- 반드시 예외처리(InterruptedException)하여 작업 재개될 수 있도록 해야 함. - 형태
- void join(): 작업이 모두 끝날 때까지 기다림.
- void join(long millis): 천분의 일초의 단위 동안 기다림.
- void join(long millis, int nanos): 천분의 일초 + 나노초의 단위 동안 기다림.
yield()
- 정의
- 남은 시간을 다음 쓰레드에게 양보하고, 자신(현재 쓰레드)은 실행 대기함.
- yield()와 iterrupt()를 적절히 사용한다면, 응답성 및 효율성을 향상시킬 수 있음.
쓰레드의 동기화(synchronization)
- 멀티 쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠수도 있음.
- 진행 중인 작업이 다른 쓰레드로부터 간섭받지 않도록 '동기화'해야 함.
cf. 쓰레드의 동기화란, 한 쓰레드가 진행 중인 작업을 다른 쓰레드가 간섭하지 못하게 막는 것을 의미함.
- 동기화하기 위해서는, 간섭받지 않아야 하는 문장들을 '임계 영역'으로 설정해야 함.
- 임계 영역은 락(lock)을 얻은 단 하나의 쓰레드만 출입이 가능함. (*객체 1개에 락(lock) 1개!!)
- synchronized로 임계 영역(critical section)을 설정하는 방법(2가지)
1) 메서드 전체를 임계 영역으로 지정함.
- 객체 1개 당 락(lock) 1개이어야 함에 따라, 메서드 내 임계 영역은 최소한으로 지정하는 것을 권장함.
public synchronized void calcSum() { ... }
2) 특정한 영역을 임계 영역으로 지정함.
synchronized(객체의 참조변수) { ... }
wait(), notify()
- 정의
- 동기화(synchronization)의 효율을 높이기 위해 사용함.
- Object 클래스에 정의되어 있으며, 동기화 블록 내에서만 사용할 수 있음. - 형태
- wait(): 객체의 lock을 풀고 쓰레드를 해당 객체의 waiting pool에 넣음.
- notify(): waiting pool에서 대기 중인 쓰레드 중 '하나'를 깨움.
- notifyAll(): waiting pool에서 대기 중인 '모든' 쓰레드를 깨움.
'LANGUAGE > Java' 카테고리의 다른 글
스트림(Stream) (1) | 2023.12.05 |
---|---|
람다식(Lambda Expression) (1) | 2023.12.03 |
애너테이션(Annotations) (0) | 2023.11.29 |
열거형(enum) (1) | 2023.11.29 |
지네릭스(Generics) (0) | 2023.11.29 |