Post

자바 Thread_8


asd

세마포어

  • 허가하고 권한을 부여함.
  • 사용자 수를 특정 리소스나 그룹을 제한하는데 사용 가능함.
  • 리소스당 사용자 하나만 허가하는 락과 달리 세마포어는 사용자에게 리소스를 제한할 수 있음.
  • 임계 영역이나 리소스마다 사용자 수를 제한할 수 있음
  • 스레드간 통신을 위한 수단!!!!
  • ex) 주차장 빈공간과 대기열


  • Lock은 허가 한 개만 줄 수 있는 세마포어라고 생각하면 편함
  • 허가를 얻다 = 리소스에 락 => 세마포어를 실행할때까지 다른 스레드는 세마포어를 얻을 수 없음

!! 세마포어는 락과 특징이 다르기때문에 락처럼 사용하면 안됨 !!

  • 소유자 스레드개념이 없음 -> 다수의 스레드가 허가를 얻기 때문 또한 스레드 하나가 세마포어를 여러번 얻을 수 있음 (초기값을 1로 맞춘 이진 세마포어는 블락)
  • 어떤 스레드든 세마포어를 사용함(세마포어를 얻지않는 스레드도 릴리스함)


생산자-소비자 시나리오 -> 세마포어를 사용해야하는상황!!

  • CPU를 최대한 활용할 수 있도록 조절해야함!
  • 스레드가 여러 개 있는 앱에 적용을 많이함
  • Actor 모델을 사용하는 스레드와 프레임워크간에 작업을 나눌 때 사용 -> TCP, UDP 패킷을 사용한 소켓채널이 대응하는 핸들러에 전달
  • 스레드간 통신 라이브러리나 프레임워크는 자주 사용함 (기본적으로 알고있으면 매우매우 좋음)
  • 스레드간 통신을 통해서 단순한 생산자-소비자 시나리오 실행이 가능함


스레드간 통신

  • 세마포어는 조건 변수의 특별한 사례
  • 스레드가 세마포어를 얻으려 하면 사용 가능한 권한의 수가 0보다 많은지를 확인하는것 -> 다음 명령어로 넘어가기위한 스레드가 요구하는 조건 => 조건이 불충족하면 다른 스레드가 세마포어 상태를 변경할 때까지 기다림
  • 다른 스레드가 실행되면 기존 스레드가 권한 조건이 충족되었는지 확인함 -> 충족됐다면 다음 명령어를 계속 실행

조건 변수 - 스레드 간 통신의 제네릭 방법

  • 연속적인 스레드 실행을 명시하기 위해 원하는 모든 조건을 사용
  • 스레드A가 조건 확인 -> 안되면 현재 스레드를 정지하고 나중에 다른 스레드가 상태를 변경해 스레드A를 깨우는 신호를 보낼때까지 기다림
  • 스레드A 조건이 충족되면 다음 단계로 넘어가 계속 실행 -> 그러다 다시 조건이 안맞으면 기다리고 다른 스레드의 신호를 기다림

    조건변수는 항상 Lock과 연관되어있음 !!

  • 락은 조건 확인과 조건에 들어있는 공유 변수의 수정이 원자적으로 실행되었는지 확인하는데 사용

생성자-소비자 시나리오 !

  • 두 개의 스레드
    1. UI스레드로 이름, 암호 부여
    2. 이름과 암호를 디비에서 검색하고 인증여부 확인
  • 디비에서 검색하는데 시간이 오래걸리고 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으로 사용할 수 가능해 스레드 간 통신에 모든 객체 사용 가능함
    1. wait() => 다른 스레드가 깨어날 때까지 현재 스레드를 기다리게 함
    • wait 상태에서는 스레드가 CPU를 사용하지않음
      1. notify() 통해서 현재 객체에서 대기하는 단일 스레드를 깨우고, 여러 스레드가 객체에서 대기하면 임의로 한개만 깨어남
      2. 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.