자바 병렬 시리즈 의 CyclicBarrier 소스 코드 분석
11129 단어 Java동시 다발 시리즈CyclicBarrier
Cyclic Barrier 류 내부 에 카운터 가 있 습 니 다.모든 스 레 드 는 장벽 에 도 착 했 을 때 await 방법 으로 자신 을 막 습 니 다.이때 계수 기 는 1 을 줄 이 고 카운터 가 0 으로 줄 었 을 때 await 방법 으로 막 힌 모든 스 레 드 가 깨 어 납 니 다.이것 이 바로 하나의 스 레 드 가 서로 기다 리 는 원 리 를 실현 하 는 것 입 니 다.다음은 Cyclic Barrier 에 어떤 멤버 변수 가 있 는 지 살 펴 보 겠 습 니 다.
//
private final ReentrantLock lock = new ReentrantLock();
//
private final Condition trip = lock.newCondition();
//
private final int parties;
//
private final Runnable barrierCommand;
//
private Generation generation = new Generation();
//
private int count;
// Generation
private static class Generation {
boolean broken = false;
}
위 에 Cyclic Barrier 의 모든 구성원 변 수 를 붙 여 놓 았 습 니 다.Cyclic Barrier 내 부 는 조건 부 대기 열 트 립 을 통 해 스 레 드 를 막 고 내부 에 두 개의 int 형 변수 parties 와 count 를 유지 하고 있 습 니 다.parties 는 차단 할 때마다 스 레 드 수 를 표시 합 니 다.이 값 은 구성 할 때 할당 합 니 다.count 는 내부 카운터 입 니 다.초기 값 은 파티 와 같 습 니 다.이후 await 방법 이 호출 될 때마다 1 을 줄 이 고 0 으로 줄 일 때 까지 모든 스 레 드 를 깨 웁 니 다.Cyclic Barrier 는 정적 내부 클래스 Generation 이 있 습 니 다.이러한 대상 은 울타리 의 현재 세 대 를 대표 합 니 다.마치 게임 을 할 때 대표 하 는 이 게임 처럼 순환 대기 가 가능 합 니 다.barrierCommand 는 교체 전에 실 행 된 임 무 를 표시 합 니 다.count 가 0 으로 줄 었 을 때 이 게임 이 끝 났 음 을 표시 하고 다음 게임 으로 넘 어가 야 합 니 다.다음 게임 으로 넘 어가 기 전에 모든 막 힌 스 레 드 를 깨 웁 니 다.모든 스 레 드 를 깨 우기 전에 지정 한 barrierCommand 를 통 해 자신의 임 무 를 수행 할 수 있 습 니 다.다음은 구조 기 를 살 펴 보 자.
// 1
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
// 2
public CyclicBarrier(int parties) {
this(parties, null);
}
Cyclic Barrier 는 두 개의 구조 기 가 있 습 니 다.그 중에서 구조 기 1 은 핵심 구조 기 입 니 다.여기 서 이 게임 의 참여 자 수(차단 할 스 레 드 수)와 이 게임 이 끝 날 때 수행 할 작업 을 지정 할 수 있 습 니 다.카운터 count 의 초기 값 이 파티 로 설정 되 어 있 는 것 도 볼 수 있 습 니 다.Cyclic Barrier 류 의 가장 주요 한 기능 은 장벽 점 에 먼저 도착 하 는 스 레 드 를 막 고 뒤의 스 레 드 를 기다 리 는 것 입 니 다.그 중에서 두 가지 기다 리 는 방법 을 제공 합 니 다.각각 정시 대기 와 비정 기적 대기 입 니 다.
//
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe);
}
}
//
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
정 해진 시간 에 기다 리 든 비정 기적 으로 기다 리 든 dowait 방법 을 사용 하 는 것 을 볼 수 있 습 니 다.들 어 오 는 매개 변수 가 다 를 뿐 입 니 다.다음은 dowait 방법 이 무엇 을 했 는 지 살 펴 보 겠 습 니 다.
//
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
//
if (g.broken) {
throw new BrokenBarrierException();
}
//
if (Thread.interrupted()) {
//
//1.
//2.
//3.
breakBarrier();
throw new InterruptedException();
}
// 1
int index = --count;
// 0
if (index == 0) {
boolean ranAction = false;
try {
//
final Runnable command = barrierCommand;
if (command != null) {
command.run();
}
ranAction = true;
//
nextGeneration();
return 0;
} finally {
//
if (!ranAction) {
breakBarrier();
}
}
}
// 0
for (;;) {
try {
//
if (!timed) {
trip.await();
}else if (nanos > 0L) {
nanos = trip.awaitNanos(nanos);
}
} catch (InterruptedException ie) {
//
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// ,
Thread.currentThread().interrupt();
}
}
//
if (g.broken) {
throw new BrokenBarrierException();
}
//
if (g != generation) {
return index;
}
//
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
위 에 붙 인 코드 중의 주석 은 모두 비교적 상세 하 므 로 우 리 는 중요 한 것 만 골 라 서 말 할 것 이다.dowait 방법 에 서 는 매번 count 를 1 로 줄 이 고 감 소 된 후 바로 0 인지 판단 하 는 것 을 볼 수 있 습 니 다.0 과 같 으 면 이전에 지정 한 임 무 를 먼저 수행 하고 실행 한 후에 nextGeneration 방법 으로 울 타 리 를 다음 세대 로 옮 깁 니 다.이 방법 에 서 는 모든 스 레 드 를 깨 워 카운터 의 값 을 파티 로 재 설정 합 니 다.마지막 으로 울 타 리 를 다시 설치 해 nextGeneration 방법 을 실행 한 뒤 게임 이 다음 판 에 들 어 가 는 것 을 의미한다.계수기 가 0 과 같 지 않 으 면 for 순환 에 들 어가 서 매개 변수 에 따라 trip.awaitNanos(nanos)를 호출 할 지,trip.awat()방법 을 호출 할 지 결정 합 니 다.이 두 가지 방법 은 정시 와 비정 기적 인 대기 에 대응 합 니 다.대기 중 현재 스 레 드 가 중단 되면 breakBarrier 방법 을 실행 합 니 다.이 방법 은 울 타 리 를 깨 는 것 이 라 고 합 니 다.게임 이 중간 에 끊 어 지 는 것 을 의미 합 니 다.generation 의 broken 상 태 를 true 로 설정 하고 모든 스 레 드 를 깨 우 는 것 을 의미 합 니 다.또한 이 는 기다 리 는 과정 에서 하나의 스 레 드 가 전체 게임 이 중단 되면 끝나 고 그 전에 막 힌 스 레 드 가 모두 깨 어 난 다 는 것 을 의미한다.스 레 드 가 깨 어 난 후 다음 세 가지 판단 을 실행 하여 breakBarrier 방법 을 사용 하여 깨 어 났 는 지,만약 그렇다면 이상 을 던 졌 는 지 확인 합 니 다.정상 적 인 세대교체 작업 으로 깨 어 났 는 지,그렇다면 카운터 의 값 을 되 돌려 줍 니 다.시간 초과 로 깨 어 났 는 지,그렇다면 브레이크 배 리 어 를 이용 해 울 타 리 를 깨 고 이상 을 던 져 보 자.여기 서 주의해 야 할 것 은 만약 에 그 중의 한 스 레 드 가 시간 을 초과 해서 종료 되면 전체 게임 도 끝나 고 다른 스 레 드 가 모두 깨 어 날 것 이다.다음은 nextGeneration 방법 과 breakBarrier 방법의 구체 적 인 코드 를 붙 입 니 다.
//
private void nextGeneration() {
//
trip.signalAll();
//
count = parties;
//
generation = new Generation();
}
//
private void breakBarrier() {
//
generation.broken = true;
//
count = parties;
//
trip.signalAll();
}
위 에서 우 리 는 이미 소스 코드 를 통 해 Cyclic Barrier 의 원 리 를 기본적으로 명확 하 게 설명 했다.다음은 우 리 는 경마 의 예 를 통 해 그것 의 사용 을 깊이 파악 할 것 이다.
class Horse implements Runnable {
private static int counter = 0;
private final int id = counter++;
private int strides = 0;
private static Random rand = new Random(47);
private static CyclicBarrier barrier;
public Horse(CyclicBarrier b) { barrier = b; }
@Override
public void run() {
try {
while(!Thread.interrupted()) {
synchronized(this) {
//
strides += rand.nextInt(3);
}
barrier.await();
}
} catch(Exception e) {
e.printStackTrace();
}
}
public String tracks() {
StringBuilder s = new StringBuilder();
for(int i = 0; i < getStrides(); i++) {
s.append("*");
}
s.append(id);
return s.toString();
}
public synchronized int getStrides() { return strides; }
public String toString() { return "Horse " + id + " "; }
}
public class HorseRace implements Runnable {
private static final int FINISH_LINE = 75;
private static List<Horse> horses = new ArrayList<Horse>();
private static ExecutorService exec = Executors.newCachedThreadPool();
@Override
public void run() {
StringBuilder s = new StringBuilder();
//
for(int i = 0; i < FINISH_LINE; i++) {
s.append("=");
}
System.out.println(s);
//
for(Horse horse : horses) {
System.out.println(horse.tracks());
}
//
for(Horse horse : horses) {
if(horse.getStrides() >= FINISH_LINE) {
System.out.println(horse + "won!");
exec.shutdownNow();
return;
}
}
//
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch(InterruptedException e) {
System.out.println("barrier-action sleep interrupted");
}
}
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(7, new HorseRace());
for(int i = 0; i < 7; i++) {
Horse horse = new Horse(barrier);
horses.add(horse);
exec.execute(horse);
}
}
}
이 경마 프로그램 은 주로 콘 솔 에서 각 경마 의 현재 궤적 을 끊임없이 인쇄 함으로써 동적 디 스 플레이 효 과 를 얻는다.전체 경 기 는 여러 차례 의 라운드 가 있 습 니 다.매 라운드 각 경 마 는 무 작위 로 몇 걸음 걸 은 후에 await 방법 으로 기다 리 고 있 습 니 다.모든 경마 가 한 라운드 가 끝 날 때 임 무 를 수행 하여 모든 경마 의 현재 궤적 을 콘 솔 에 인쇄 합 니 다.이렇게 매 라운드 에서 각 경마 의 궤적 이 끊임없이 증가 하고 있다.그 중에서 어떤 경마 의 궤적 이 가장 먼저 지 정 된 값 으로 증가 할 때 전체 경 기 를 끝 낼 것 이다.이 경 마 는 전체 경기 의 승리자 가 될 것 이다!프로그램의 실행 결 과 는 다음 과 같다.이로써 우 리 는 Cyclic Barrier 와 Count Downlatch 를 비교 하 는 것 을 피하 기 어렵다.이 두 가지 유형 은 하나의 스 레 드 가 특정한 조건 에 도달 하기 전에 기다 리 는 것 을 실현 할 수 있 습 니 다.내부 에 하나의 카운터 가 있 습 니 다.카운터 의 값 이 0 으로 계속 줄 어 들 면 모든 막 힌 스 레 드 가 깨 어 납 니 다.차이 점 은 Cyclic Barrier 의 계수 기 는 스스로 제어 하고 Count Downlatch 의 계수 기 는 사용자 가 제어 합 니 다.Cyclic Barrier 에서 스 레 드 호출 await 방법 은 자신 을 막 을 뿐만 아니 라 계수 기 를 1 로 줄 일 수 있 습 니 다.Count Downlatch 에서 스 레 드 호출 await 방법 은 자신 을 막 을 뿐 카운터 의 값 을 줄 이지 않 습 니 다.또 Count Downlatch 는 한 바퀴 만 차단 할 수 있 고,Cyclic Barrier 는 순환 차단 이 가능 하 다.일반적으로 Cyclic Barrier 를 사용 하면 Count Downlatch 의 기능 을 실현 할 수 있 지만,반대로 위의 경마 프로그램 은 Cyclic Barrier 만 사용 할 수 있다.한 마디 로 하면 이 두 가지 공통점 은 대체적으로 이와 같다.언제 Cyclic Barrier 를 사용 하 는 지,언제 Count Downlatch 를 사용 하 는 지 에 대해 서 는 독자 스스로 파악 해 야 한다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.