Post
EN

블로킹 메소드, 인터럽터블 메소드

스레드는 여러가지 원인에 의해 블록 당하거나, 멈춰질 수 있다.

I/O 작업이 끝나기를 기다리는 경우.

락을 확보하기 위해 기다리는 경우.

Thread.sleep 메소드가 끝나기를 기다리는 경우.

다른 스레드가 작업 중인 내용의 결과를 확인하기 위해 기다리는 경우.

쓰레드 블록된 상태 (BLOCKED, WAITING, TIMED_WAITING).

BlockingQueue 인터페이스의 put과 take 메소드는 Thread.sleep 메소드와 같이 InterruptedException을 발생시킬 수 있다.

인터럽트는 스레드가 서로 협력해서 실행하기 위한 방법이다.

InterruptedException이 발생했을 때 그에 대처할 수 있는 방법은 두가지가 있다.

InterruptedException을 전달

받아낸 InterruptedExceptioon을 그대로 호출한 메소드에게 넘겨버리는 방법이다.

인터럽트를 무시하고 복구

특정상황에서는 InterruptedException을 throw할 수 없을 경우, 예를 들어 Runnable 인터페이스를 구현한 경우가 해당된다.

이런 경우 InterruptedException을 catch한 다음, 현재 스레드의 interrupt 메소드를 호출해 인터럽트 상태를 설정, 상위 호출 메소드가 인터럽트 상황에 있음을 알 수 있도록 해야 한다.

인터럽트를 잘 활용하면 훨씬 세밀하게 고급 기능을 구현할 수 있지만 위의 두가지 방법을 사용하면 대부분의 경우에 대응할 수 있다.

Thread.currentTread().interrupt();

동기화 클래스

블로킹 큐는 다양한 컬렉션 클래스 가운데 특히나 눈에 띄는 특성이 많다. 단순히 객체를 담을 수 있는 컬렉션 클래스라는 것뿐만 아니라, 프로듀서와 컨슈머 사이에 take와 put등의 블로킹 메소드를 사용해 작업 흐름을 조절할 수 있기 때문이다.

(-_-… 바꿔야겠다 이전에 HashMap 으로 queue만들었었는데 바보 같다..)

상태 정보를 사용해 스레드 간의 작업 흐름을 조절할 수 있도록 만들어진 모든 클래스를 동기화 클래스(synchronizer)라고 한다.

동기화 클래스 예로는 세마포어 semaphore, 배리어 barrier, 래치 latch등이 있다.

래치(latch)

래치는 스스로 터미널 상태에 이를 때까지의 스레드가 동작하는 과정을 늦출 수 있도록 해주는 동기화 클래스이다. 일종의 관문과 같은 형태로 동작. 터미널 상태에 이르기 전에는 어떤 스레드도 통과할 수 없다.

이런 특징으로 다음과 같이 사용할 수 있다.

  • 특정 자원을 확보하기 전에는 작업을 시작하지 말아야 하는 경우에 사용할 수 있다.

  • 의존성을 갖고 있는 다른 서비스가 시작하기 전에는 특정 서비스가 실행되지 않도록 막아야 하는 경우에 사용할 수 있다.

  • 특정 작업에 필요한 모든 객체가 실행할 준비를 갖출 때까지 기다리는 경우에도 사용할 수 있다.

CountDownLatch는 하나 또는 둘이상의 스레드가 여러 개의 이벤트가 일어날 때까지 대기할 수 있도록 되어있다. 양의 정수값으로 카운터를 초기화하며, 이 값은 대기하는 동안 발생해야 하는 이벤트의 건수를 의미한다.

await 메소드는 래치 내부의 카운터가 0이 될 때까지, 즉 대기하던 이벤트가 모두 발생했을 때까지 대기하도록 하는 메소드이다.

FutureTask

FutrueTask 역시 래치와 비슷한 형태로 동작한다.

시작전 대기, 시작됨, 종료됨 과같은 세가지 상태를 가지고 있다.

FutureTask가 한 번 종료됨 상태에 이르고 나면 더 이상 상태가 바뀌는 일은 없다.

세마포어

카운팅 세마포어 (counting semaphore)는 특정 자원이나 특정 연산을 동시에 사용하거나 호출할 수 있는 스레드의 수를 제한하고자 할 때 사용한다.

이런 기능을 사용하면 자원 풀(pool)이나 컬렉션의 크기에 제한을 두고자 할 때 유용하다.

Semaphore 클래스는 가상의 퍼밋(permit)을 만들어 내부 상태를 관리하며, Semaphore를 생성할 때 생성 메소드에 최초로 생성할 퍼밋의 수를 넘겨준다. 외부 스레드는 퍼밋을 요청해 확보하거나, 이전에 확보한 퍼밋을 반납할 수 있다.

acquire 메소드는 남는 퍼밋이 생기거나, 인터럽트가 걸리거나, 지정한 시간을 넘겨 타임아웃이 걸리기 전까지 대기한다.

release 메소드는 확보했던 퍼밋을 다시 세마포어에게 반납하는 기능을 한다.

카운팅 세마포어를 좀더 간단한 형태로 살펴보자면 이진 세마포어를 생각해 볼 수 있는데, 이진 세마포어는 초기 퍼밋 값이 1로 지정된 카운팅 세마포어이다.

이전 세마포어는 비재진입(nonreentrant)락의 역할을 하는 뮤텍스(mutex)로 활용할 수 있다.

이진 세마포어의 퍼밋을 갖고 있는 스레드가 뮤텍스를 확보한 것이다.

배리어

배리어(barrier)는 특정 이벤트가 발생할 때까지 여러 개의 스레드를 대기 상태로 잡아둘 수 있다는 측면에서 래치와 비슷하다고 볼 수 있다.

래치는 이벤트를 기다리기 위한 동기화 클래스이고, 배러이는 다른 스레드를 기다리기 위한 동기화 클래스이다.

베리어 포인트에 다다르면 await 메소드를 호출하며, await 메소드는 모든 스레드가 배리어 포인트에 도달할 때까지 대기한다.

모든 스레드가 배리어 포인트에 도달하면 배리어는 모든 스레드를 통과시키며, await 메소드에서 대기하고 있던 스레드는 대기 상태가 모두 풀려 실행되고, 배리어는 다시 초기 상태로 돌아가 다음 배리어 포인트를 준비한다.

일부 면적만 별도의 스레드로 계산시키고 이후에 취합해서 계산하는 형태라고 한다.

(신기 -_-;;)

This article is licensed under CC BY 4.0 by the author.