이후 이미 lock을 얻은 스레드고 unlock()을 호출하면 block 된 스레드들이 runnable 상태로 돌아가서 lock 을 놓고 경쟁을 벌인다.
한 스레드만 lock을 획득하고 Critical Section 을 수행한 후 finally 구문에 의해서 역시 unlock() 이 호출됨을 보장받는 식으로 동기화를 수행한다.
간단한 예제로 "숫자탑 쌓기" 프로그램을 만들어서 사용법을 익혀봄.
이 프로그램은 0 에서 n-1 까지 n 개의 숫자를 임의로 섞어서 NumberPart 라는 클래스들에게 임의로 할당한다. m 개의 NumberPart 클래스는 정렬이 되지 않은 숫자 집합들을 가지게 되고 자신들이 가진 숫자를 순서에 맞게 차곡차곡 NumberTower 에 쌓는 식.
이를 위해서 동기화가 이뤄져야 하는데 각 NumberPart는 현재 NumberTower에 올려진 숫자를 보고 자신이 그 다음 숫자를 가지고 있는지 확인한 후 가지고 있으면 그 숫자를 반환한 수 lock을 다른 클래스들에게 넘기는 식으로 수행된다.
곰곰히 생각해보면 이 예제는 동기화가 필요하지 않다. 왜냐하면 현재 타워에 올려진 i 다음 숫자인 i+1을 가진 NumberPart 스레드는 단 하나이기 때문. 물론 구현을 바꾸면 동기화가 필요할 수도 있지만 lock의 사용법을 익혀보기 위해서 만들어본 예제임.
스레드간 동기화를 조율하는 코드는 한 클래스 내에 몰아넣는게 편하다. 예제에서도 NumberPart에는 아무런 동기화 관련 코드가 없다. 실제로 동기화를 관리하는 NumberTower 에 lock, unlock 코드가 몰려있다.
BoundedBuffer에 접근하는 두 집단의 스레드들(데이터를 넣는 스레드들, 데이터를 빼내는 스레드들)에게 각각 별도의 Condition 인스턴스에서 WAIT 하게 한다.
빼갈 데이터가 없는 상태에서 데이터를 빼가려는 스레드가 접근할 경우 notEmpty 에서 WAIT 하게 한다. 나중에 데이터를 넣는 스레드들 중 하나가 lock을 얻어서 버퍼에 데이터를 입력한 후 notEmpty.signalAll(); 을 호출하면 WAIT하고 있던 스레드들이 모두 runnable 상태로 돌아간다.(이 스레드들은 모두 데이터를 빼내려는 스레드들이다).
이 기능을 NumberTower에 적용해서 숫자탑 쌓는데 걸린 시간을 출력하려는 스레드를 WAIT 상태에서 묶어놓을 수 있다.
public class ReentrantLockTest {
.......
public static void main(String[] args) {
......... tower.getElapsedTime();
}
}
위에서 getElapedTime(); 을 호출하는 스레드는 메인 스레드임에 유의해야함.
메인스레드는 경과 시간을 출력하기 위해서 lock을 얻은 후 곧바로 WAIT 상태로 들어간다.
class NumberTower {
public void getElapsedTime(){
lock.lock();
try { timeNotificationCondition = lock.newCondition();
while ( current < top ) {
System.out.println("wating.............."); timeNotificationCondition.await();
}
System.out.println("elapsed " + (System.currentTimeMillis() - startTime));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
탑 쌓기가 다 끝난 후
class NumberTower ........
public void pile ( NumberPart np ){
lock.lock();
try {
if ( np.hasValue(current )){
np.remove(current);
current++;
} if ( current == top ) timeNotificationCondition.signalAll(); } finally {
lock.unlock();
}
}
condition 인스턴스의 signallAll(); 을 호출해서 메인스레드를 runnable 상태로 되돌린다. while 루프를 빠져나왔을때는 이미 탑쌓기가 끝났으므로 경과 시간을 출력할 수 있다.
1.5 이전에 Object.wait(); 관련 메소드를 호출하기 위해서 먼저 모니터를 획득해야하는 것처럼 위에서도 lock을 획득한 후에 java.util.concurrent.locks.Condition.await(); 를 호출해야한다.(안그러면 예외 발생)
출처 : javafreak.tistory.com -> 이분, 블로그 정신이 참 좋다. 니꺼내꺼 없지정신.ㅋㅋㅋㅋㅋㅋ
댓글