자바 Thread_8
asd
세마포어
- 허가하고 권한을 부여함.
- 사용자 수를 특정 리소스나 그룹을 제한하는데 사용 가능함.
- 리소스당 사용자 하나만 허가하는 락과 달리 세마포어는 사용자에게 리소스를 제한할 수 있음.
- 임계 영역이나 리소스마다 사용자 수를 제한할 수 있음
- 스레드간 통신을 위한 수단!!!!
- ex) 주차장 빈공간과 대기열
- Lock은 허가 한 개만 줄 수 있는 세마포어라고 생각하면 편함
- 허가를 얻다 = 리소스에 락 => 세마포어를 실행할때까지 다른 스레드는 세마포어를 얻을 수 없음
!! 세마포어는 락과 특징이 다르기때문에 락처럼 사용하면 안됨 !!
- 소유자 스레드개념이 없음 -> 다수의 스레드가 허가를 얻기 때문 또한 스레드 하나가 세마포어를 여러번 얻을 수 있음 (초기값을 1로 맞춘 이진 세마포어는 블락)
- 어떤 스레드든 세마포어를 사용함(세마포어를 얻지않는 스레드도 릴리스함)
생산자-소비자 시나리오 -> 세마포어를 사용해야하는상황!!
- CPU를 최대한 활용할 수 있도록 조절해야함!
- 스레드가 여러 개 있는 앱에 적용을 많이함
- Actor 모델을 사용하는 스레드와 프레임워크간에 작업을 나눌 때 사용 -> TCP, UDP 패킷을 사용한 소켓채널이 대응하는 핸들러에 전달
- 스레드간 통신 라이브러리나 프레임워크는 자주 사용함 (기본적으로 알고있으면 매우매우 좋음)
- 스레드간 통신을 통해서 단순한 생산자-소비자 시나리오 실행이 가능함
스레드간 통신
- 세마포어는 조건 변수의 특별한 사례
- 스레드가 세마포어를 얻으려 하면 사용 가능한 권한의 수가 0보다 많은지를 확인하는것 -> 다음 명령어로 넘어가기위한 스레드가 요구하는 조건 => 조건이 불충족하면 다른 스레드가 세마포어 상태를 변경할 때까지 기다림
- 다른 스레드가 실행되면 기존 스레드가 권한 조건이 충족되었는지 확인함 -> 충족됐다면 다음 명령어를 계속 실행
조건 변수 - 스레드 간 통신의 제네릭 방법
- 연속적인 스레드 실행을 명시하기 위해 원하는 모든 조건을 사용
- 스레드A가 조건 확인 -> 안되면 현재 스레드를 정지하고 나중에 다른 스레드가 상태를 변경해 스레드A를 깨우는 신호를 보낼때까지 기다림
- 스레드A 조건이 충족되면 다음 단계로 넘어가 계속 실행 -> 그러다 다시 조건이 안맞으면 기다리고 다른 스레드의 신호를 기다림
조건변수는 항상 Lock과 연관되어있음 !!
- 락은 조건 확인과 조건에 들어있는 공유 변수의 수정이 원자적으로 실행되었는지 확인하는데 사용
생성자-소비자 시나리오 !
- 두 개의 스레드
- UI스레드로 이름, 암호 부여
- 이름과 암호를 디비에서 검색하고 인증여부 확인
- 디비에서 검색하는데 시간이 오래걸리고 1번 스레드에서 확인이 어려움
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
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
String username = null, password = null; // 공유변수
/* 인증스레드 */
lock.lock(); // 락을 얻고
try {
// 변수가 값을 얻었는지 확인후, 없으면 다른 스레드가 깨울때까지 슬립함 (await로 인해 깨울때까지 기다림 -> lock을 원자적으로 언락함)
while(username == null || password == null) {
condition.await();
}
}
finally {
lock.unlock();
}
/* UI스레드 */
lock.lock(); // 인증스레드가 잠기면 락을 얻음
try {
// 사용자가 입력안하면 null이 되고, 입력하면 인증스레드에서 await된 스레드를 깨우기위해 signal 호출
username = geName();
password = getPassword();
condition.signal();
}
finally {
lock.unlock(); // 언락이 실행될때까지 인증스레드는 락을 얻지못함
}
- await메서드는 스레드를 완전히 슬립시켜 다른 스레드가깨울때까지 CPU를 포기함
- signal메서드는 조건변수에서 기다리는 스레드를 깨우는데, 조건 변수에서 기다리는 스레드가 하나 이상일 때 하나만 깨어나고 나머지는 계속 슬립 -> 깨어난 스레드는 조건 변수와 연관된 lock을 다시 얻어야함.
- 조건 변수에 기다리는 스레드가 없을 때는 신호가 가는 스레드도 없음 (세마포어와 조건 변수의 아주 중요한 동작 차이)
- 조건 변수가 신호를 받는 사실이 어느곳에도 저장되지않음!!
스레드간 통신 - 방법 2
- Java의 모든 객체 클래스에는 wait(), notify(), notifyAll()가 존재함
- 어떤 유형의 객체에서도 해당 메서드를 호출할 수 있음 => 프로그램의 어떤 객체라도 조건 변수로 사용이 가능함
- synchronized 키워드를 사용해 모든 객체를 Lock으로 사용할 수 가능해 스레드 간 통신에 모든 객체 사용 가능함
- wait() => 다른 스레드가 깨어날 때까지 현재 스레드를 기다리게 함
- wait 상태에서는 스레드가 CPU를 사용하지않음
- notify() 통해서 현재 객체에서 대기하는 단일 스레드를 깨우고, 여러 스레드가 객체에서 대기하면 임의로 한개만 깨어남
- notifyAll() 호출하면 객체의 모든 스레드를 깨움
전제조건은 꼭 객체를 동기화해야함 !!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class mainClass {
// 완료 상태를 나타내는 변수
private boolean isComplete = false;
public void waitUntilComplete() {
synchronized(this) {
// 참이 아니면 스레드는 wait상태가 되고 CPU를 포기함
while(isComplete == false) {
this.wait();
}
}
}
public void complete() {
synchronized(this) {
// 이후 해당 객체로 오게되면 조건변수를 참으로 변경후 wait상태 스레드를 깨움
isComplete = true;
this.notify();
}
}
}
// 현재 객체만을 사용할거면 함수 synchronized 선언가능
출처 - https://kmooc.udemy.com/course/java-multi-threading/ (Java 멀티스레딩, 병행성 및 성능 최적화 - 전문가 되기 )
This post is licensed under CC BY 4.0 by the author.