프로세스 스케줄링
- 선점형(Preemptive)
: 대기중인 프로세스가 갖는 컴퓨터의 자원(CPU 사이클)을 우선적으로 차지할 수 있는 권한
- 비선점형(Non-Preemptive)
: 프로세스가 자발적으로 자원을 반납하기 전까지는 CPU 사용권이 넘어가지 않음
스케줄링 알고리즘
FCFS(First Come First Serve)
- 비 선점형 스케줄링 기법으로 먼저 도착한 순서에 따라 처리
- 프로세스의 처리 시간이 길 경우, 대기 프로세스의 대기 시간이 길어지는 단점
SJF(Shortest Job First)
- 비 선점형 스케줄링 기법으로 실행 시간이 가장 짧은 순서대로 처리
- 실행 시간이 긴 프로세스는 계속 밀림 -> 무한 대기가 발생할 수 있음
HRN(Highest Response-ratio Next)
- 비 선점형 스케줄링 기법으로 SJF에서 발생할 수 있는 무한대기 문제를 해결한 기법
- 시스템 응답 시간이 큰 프로세스 우선 처리
Priority Scheduling
- 선점형/ 비 선점형 스케줄링 기법으로 특정 규칙에 따라 우선순위를 매겨서 실행시킴
- 비선점형의 경우 우선 순위가 낮으면 무한 대기가 발생할 수 있음
- 선점형의 경우 실행에 따라 조건이 변경되어 우선 순위가 높아진 프로세스가 프로세서를 빼앗아 올 수 있음
Round Robin Scheduling
- 선점형 스케줄링 기법으로 프로세스의 종료 여부와 관계 없이 일정 시간 간격으로 돌아가면서 모든 프로세스가 실행됨
- 프로세스 전환 시간이 짧으면 오버헤드가 많아지는 단점이 있음
Java에서 스레드를 구현하려면?
첫번째 방법. Thread 클래스 상속 : 자바에서는 클래스의 다중 상속을 지원하지 않으므로 다른 클래스의 상속이 불가능하다🙄 But 객체 생성 후 바로 실행 가능하다는 장점이 있다!
이때, Thread 클래스를 상속하면 run()
메소드를 꼭 오버라이딩 해줘야한다.
두번째 방법. Runnable 인터페이스 : 다른 클래스에서 상속을 통해 구현 가능하나 객체 생성 후 바로 사용할 수 없고 추가적인 Thread 객체가 요구된다.
Ex) TestThread test = new TestThread("test");
Thread thread = new Thread(test);
단일 스레드
- Main 스레드에서 작업 진행, 작업은 순차적으로
- 하나의 프로세스에서 오직 하나의 스레드로만 실행
: 단일 레지스터와 스택으로 구성
- Context Switching 작업을 요구하지 않음
- 동시성 제어에 대한 처리 신경쓰지 않아도 됨
멀티 스레드
- 프로그램 내에서 두 개 이상의 동작을 동시에 실행
: 프로세서의 활동 극대화, 두 개가 동작을 동시에 실행
✔ 이때 말하는 동시는 우리가 흔히 생각하는 동시가 아니다. 그럼 무엇일까?
(내가 생각하는 이유로는) 멀티 스레드에서 짧은 시간동안 번갈아가면서 동작을 하기 때문에 동시에 이루어지지 않지만 우리가 겉에서 볼 땐 동시에 동작하는 것처럼 보이기도 한다.
Thread 상태
- New
스레드가 실행 준비를 완료한 상태로, start()
메소드를 호출하기 전 상태
- Runnable
start()
가 호출되어 실행될 수 있는 상태
- Wait
다른 스레드가 통지할 때까지 기다리는 상태
- Timed_Wait
정해진 시간동안 기다리는 상태
- Blocked
사용하고자 하는 객체의 잠금(lock)이 풀릴 때까지 대기하는 상태
- Terminated
실행이 종료된 상태
외부에서 스레드 상태 변경을 위한 메소드
interrupt() 메소드
- 스레드가 일시 정지 상태에 있을 때, InterruptException 예외를 발생시키는 역할
sleep()
이 있을때만 사용할 수 있음
wait() 메소드
- 동기화 영역에서 lock을 풀고 Wait-Set 영역으로 이동
- lock을 소유한 스레드가 자신의 제어권을 양보하고 WAITING 또는 TIMED_WAITING 상태에서 대기하기 위해서 사용
notify(), notifyAll() 메소드
notify()
는 하나, notifyAll()
은 Wait-Set 영역에 있는 전부를 깨운다.
notify()
는 Wait-Set 영역에서 대기중인 스레드중 랜덤하게 깨움
notify()
를 사용하면 synchronized
를 써줘야 실행된다.
notifyAll()
을 사용하게 되면 synchronized
에서는 하나만 실행할 수 있으므로 실행되지 않는 나머지는 Blok
상태가 된다.
sleep(long millis), sleep(long millis, int nanos) 메소드
- 지정된 시간 동안 쓰레드 일시 정지
- 지정된 시간이 지나고 나면 다시 실행 상태
interrupt
가 걸리기 위한 조건으로 interrupted()
를 사용하여 체크(sleep
이 없으면 interrupt
이 걸리지 않는 것을 확인하기 위해 busy waiting
을 사용)
join() 메소드
join()
: 호출한 스레드가 종료될 때까지 기다림
join(long millis)
: 호출한 스레드를 지정된 시간동안만 기다림
join(long millis, int nanos)
: 호출한 스레드를 지정된 시간동안 기다림, 대기 시간은 나노초 단위로 제어
Ex) thread1.join();
yield() 메소드
static
메소드
- 현재 실행 중인 스레드를 중지시켜 동일한 우선 순위의 다른 대기 스레드를 동작시킬 수 있음 -> 동일한 우선 순위가 없는 경우, 다시 실행
- 하나의 스레드가 프로세서를 과도하게 점유하지 않도록 조절
Thread 동기화(synchronized)
- 동기화는 연산이 겹치면 안되는 부분에 사용한다.
첫번째 방법. 메소드 동기화(메소드 전체)
public synchronized void 메소드() { 내용.. }
두번째 방법. 구간 동기화(특정 구간만)
public void 메소드() {
내용..
synchronized (this) {
동기화 할 내용..
}
}
임계구역(Critical Section)
- 병렬 컴퓨팅에서 두 이상의 스레드가 동시에 접근해서는 안되는 공유 자원을 접근하는 코드의 일부
- 공유 데이터를 사용하는 코드 영역을 임계구역으로 지정해놓고 공유 데이터(객체)가 가지고 있는
lock
을 획득한 단 하나의 스레드만 이 영역 내의 코드를 수행할 수 있게 한다. 그리고 해당 스레드가 임계구역 내의 모든 코드를 수행하고 lock
을 반납해야 다른 스레드가 반납된 lock
을 획득하여 임계구역의 코드를 수행할 수 있게 된다.