스레드 상태를 직접적으로 관리할 수 있는 메소드를 공부한 내용을 기록한다.
스레드는 실행대기 - 실행 - 종료의 생명주기를 가진다.
이 상태체계를 정리하면 하기와 같다.
스레드 상태제어는 스레드 각각의 상태를 제어하는 것이 아니라, 스레드의 흐름을 제어하는 static(정적) 제어임을 명확히 이해한다.
멀티 스레드의 경우 자원을 공유하는데, 이로 인해 자원 교착상태(데드락) 및 자원경쟁시점의 차이로 인한 정합성이 저해되는 상황이 발생할 수 있다.
이러한 충돌상황이나 오류를 방지하기 위해 동기화(synchronized) 작업을 진행해야 한다.
DBMS 차원에서 자원 자체에 대한 접근을 막는 것을 비관락 혹은 낙관락이라 하며, 애플리케이션 스레드 혹은 로직(쉽게 말하면 메서드) 차원의 접근을 막는 것을 임계영역 설정이라 한다.
임계영역에는 Lock을 가진 하나의 스레드만 출입이 가능하며, 이를 설정할 수 있는 방법에는 다음과 같은 방법이 존재한다.
이때 참조변수는 스레드 내부에서, 해당 스레드 자체를 지칭하는 this로도 인수 활용이 가능하다.
notify, wait의 경우 스레드의 작업대기 및 실행전환 시점이 맞지 않거나 특정 스레드의 자원점유로 인해 다른 스레드의 자원점유가 진행되지 않을 경우, 자원점유가 이루어지지 않는 교착상태가 발생할 수 있다.
따라서 notify를 활용한다면 어떠한 스레드에 적용할지 명시해주어야 한다(그렇지 않는다면 waiting pool의 임의의 스레드에만 통지).
동기화 키워드(synchronized) 사용 시 임계영역 설정을 같은 메서드 내에서만 할 수 있는데, 멀티스레드에서 이러한 임계영역을 공유할 수 있는 장치가 필요하다. 이것이 Lock이다.
public class MyClass {
private Object lock1 = new Object();
private Object lock2 = new Object();
public void methodA() {
synchronized (lock1) {
methodB();
}
}
public void methodB() {
synchronized (lock2) {
// do something
methodA();
}
}
}
메서드A를 호출하면 lock1을 가진 상태에서 메서드B를 호출한다. 이때 메서드B는 lock2를 가진 상태에서 lock1이 풀릴때까지 기다리기에 서로 자원점유가 이루어지지 않는 데드락 상태가 발생할 수 있다.
이 상태에서 ReentrantLock을 사용하면 락을 가지더라도 실행할 수 있는 조건을 명시할 수 있다.
Read Lock과 Write Lock을 별도로 제공하며, 읽기는 공유하고 쓰기는 배타Lock을 제공하는 Lock이다.
읽기 Lock은 중복 Lock을 설정할 수 있는 반면, 읽기 Lock이 걸려있는 상태에서는 쓰기 Lock을 설정할 수 없다(데이터 변경 방지).
ReentrantReadWriteLock에 낙관락 기능을 제공, 즉 데이터를 실제로 변경할 때만 lock을 건다. 쓰기가 빈번하지 않고 데이터 변경 시 충돌이 적을 경우 혹은 그 위험성이 없다고 판단하였을 경우에 빠른 변경처리를 위해 사용하는 lock의 일종이다.
wait(), notify()에서 waiting pool에 있는 스레드를 구분할 수 없는 문제를 해결하기 위한 방안이 Condition이며, await()와 signal() 메서드를 사용한다.
따라서 특정 조건이 만족하였을 경우에만 Lock을 해제할 수 있고, ReentrantLock과 같이 활용할 수 있는 키워드이다.
(*condition1.await() or condition1.signal())