실제로, 우리는 종종 그러한 상황에 직면합니다. 활동을 수행하기 전에 시작하기 전에 모든 사람들이있을 때까지 기다려야합니다. 예를 들어, 식사를 할 때, 당신은 계속하기 전에 온 가족이 자리에 올 때까지 기다려야합니다. 여행 할 때, 당신은 출발하기 전에 모든 사람이 여기에 올 때까지 기다려야하며, 선수들이 코트에있을 때는 선수가 법정에 출발 할 때까지 기다려야합니다. JUC 패키지는 이러한 종류의 시나리오를 잘 시뮬레이션 할 수있는 동기화 도구 클래스를 제공합니다. Cyclicbarrier 클래스를 사용하여 스레드 그룹을 구현하여 서로를 기다린 다음 모든 스레드가 특정 장벽 지점에 도달 할 때 후속 작업을 수행 할 수 있습니다. 다음 그림은이 과정을 보여줍니다.
Cyclicbarrier 클래스 내부에는 카운터가 있습니다. 각 스레드는 차선 방법을 호출하여 배리어 포인트에 도달하면 스스로 차단됩니다. 이 시점에서 카운터는 1만큼 감소합니다. 카운터가 0으로 감소하면 차선 방법을 호출하여 차단 된 모든 스레드가 깨어납니다. 이것이 서로 대기하는 스레드 세트를 구현하는 원칙입니다. 먼저 Cyclicbarrier가 가지고있는 멤버 변수를 살펴 보겠습니다.
// 동기식 작동 잠금 개인 최종 재 렌트 링크 잠금 = 새로운 재 렌트 링크 락 (); // 스레드 인터셉터 비공개 최종 조건 트립 = lex.newCondition (); // 개인 최종 파티를 가로 채는 스레드 수; // 대체 전에 실행되기 전에 실행 된 스레드 수; GenerationPrivate 정적 클래스 생성 {부울 Broken = False;}Cyclicbarrier의 모든 멤버 변수는 위에 게시됩니다. 내부 Cyclicbarrier는 조건부 대기열 트립을 통해 스레드를 차단하고 두 개의 int-type 변수 파티와 계산을 유지합니다. 당사자는 매번 가로 채는 스레드 수를 나타냅니다.이 값은 시공 중에 할당됩니다. Count는 내부 카운터이며 초기 값은 당사자와 동일하며, 차선 방법의 각 호출마다 0으로 줄어들고 모든 스레드가 깨어날 때까지 1 씩 감소합니다. Cyclicbarrier는 정적 내부 클래스 생성을 보유하고 있으며이 클래스의 객체는 게임을 할 때 게임이 대표하는 게임과 마찬가지로 현재 세대의 울타리를 나타냅니다. BarrierCommand는 교체 전에 실행 된 작업을 나타냅니다. Count가 0으로 줄어드면 게임이 끝났고 다음 게임으로 전송해야 함을 의미합니다. 다음 게임에 가기 전에 모든 차단 스레드가 깨어납니다. 모든 스레드를 깨우기 전에 BarrierCommand를 지정하여 고유 한 작업을 수행 할 수 있습니다. 다음으로 우리는 생성자를 살펴 봅니다.
// 생성자 1public cyclicbarrier (int parties, runnable barrieraction) {if (당사자 <= 0) 새로운 불법 불법 행위 렉싱 (); this.parties = 당사자; this.count = 당사자; this.barriercommand = 장벽;} // 생성자 2public cyclicbarrier (int parties) {this (당사자, null);}Cyclicbarrier에는 두 개의 생성자가 있으며, 여기서 생성자 1은 핵심 생성자입니다. 여기에서는이 게임의 참가자 수 (가로 채울 스레드 수) 와이 게임이 끝날 때 실행될 작업을 지정할 수 있습니다. 카운터 카운트의 초기 값이 당사자에게 설정되어 있음을 알 수 있습니다. Cyclicbarrier 클래스의 주요 기능은 먼저 배리어 포인트에 도달 한 스레드를 차단하고 후속 스레드를 기다리는 것입니다. 그것은 대기 방법, 즉 시간이 지정된 대기 및 시간이 다되지 않는 대기 방법을 제공합니다.
// 타임 타임 대기 대기 대기 int await ()가 중단 exception, brokenbarrierexception {try {return dowait (false, 0l); } catch (timeoutException Toe) {Throw New Error (Toe); }} // 시간이 지남에 따라 대기 대기 int 대기 (긴 타임 아웃, TimeUnit Unit)는 InterruptedException, BrokenbarrierException, TimeOutexception {return dowait (true, init.tinute (timeout));}시간이 정해지거나 시간이 지남에 관계없이 대기 중지 여부에 관계없이 Dowait 방법을 호출하지만 전달 된 매개 변수는 다릅니다. Dowait 방법이 무엇을하는지 살펴 보겠습니다.
// 코어 대기 방법 개인 int dowait (부울 타임, 긴 나노)가 중단 지출, BrokenbarrierException, timeOutexception {최종 재 렌트 링크 잠금 = this.lock; lock.lock (); {최종 생성 g = Generation; // 현재 울타리가 두드려 if (g.broken) {throw new brokenbarrierexception (); } // 현재 스레드가 중단되는지 (thread.terrupted ()) {// 현재 스레드가 중단되면 다음 세 가지 작업이 완료됩니다. 현재 울타리를 날려 버립니다 // 2. 차단 된 모든 스레드 // 3. 인터럽트 예외 BreakBarrier ()를 던지십시오. 새로운 InterruptedException ()을 던지십시오. } // 매번 카운터 값을 1 int index = -카운트로 줄입니다. // 카운터 값을 0으로 줄이면 모든 스레드가 깨어나 다음 세대로 변환해야합니다. 시도 {// 모든 스레드를 깨우기 전에 지정된 작업을 실행합니다. 최종 실행 가능한 명령 = BarrierCommand; if (명령! = null) {command.run (); } RANACTION = TRUE; // 모든 스레드를 깨우고 차세대 NextGeneration ()로 이동합니다. 반환 0; } 마침내 {// 모든 스레드가 깨어날 수 있는지 확인하십시오. }}} // 카운터가 0이 아닌 경우 (;;) {try {// timed 또는 timed if (! timed) {trip.await (); } else if (nanos> 0l) {nanos = trip.awaitnanos (nanos); }} catch (InterruptedException IE) {// 대기 중 현재 스레드가 중단되면 울타리를 무너 뜨려 다른 스레드를 깨우십시오 (g == generation &&! g.broken) {breakbarrier (); IE를 던지십시오. } else {// 인터럽트 예외를 포착하기 전에 울타리의 대기가 완료되면 인터럽트 작동을 직접 스레드라고합니다 .CurrentThread (). 인터럽트 (); }} // 전복 작업으로 인해 스레드가 깨어나면 (g.broken) {throw new brokenbarrierexception (); } // 교체 작업으로 인해 스레드가 깨어나면 (g! = generation) {return index; } // 시간 때문에 스레드가 깨어나면 울타리를 두드리고 (timed && nanos <= 0l) {breakbarrier (); 새로운 TimeOutException ()을 던지십시오. }}} 마침내 {lock.unlock (); }}위에 게시 된 코드의 의견은 매우 상세하므로 중요한 내용 만 선택할 것입니다. 다우이트 방법에서 수가 매번 1 씩 감소 함을 알 수 있습니다. 감소 후 즉시 0과 같은지 확인하기로 결정됩니다. 0과 같으면 먼저 이전에 지정된 작업을 실행합니다. 실행 후 NextGeneration 방법을 호출하여 울타리를 다음 세대로 옮깁니다. 이 방법에서는 모든 스레드가 깨어나고 카운터 값이 파티에 재설정되고 펜스 생성이 재설정됩니다. NextGeneration 메소드를 실행 한 후 게임이 다음 게임에 들어갑니다. 이 시점에서 카운터가 0과 같지 않으면 for 루프로 들어갑니다. Trip.aWaitnanos (Nanos) 또는 Trip.await () 메소드를 호출할지 여부를 결정하십시오. 이 두 가지 방법은 타이밍 및 비 타임 대기에 해당합니다. 대기 중에 현재 스레드가 중단되면 Breakbarrier 메소드가 실행됩니다. 이 방법을 울타리를 깨는 것은 게임이 반쯤 잘라 내고 깨진 생성 상태를 True로 설정하고 모든 스레드를 깨우는 것을 의미합니다. 동시에, 이것은 대기 과정에서 스레드가 중단되고 전체 게임이 끝나고 이전에 차단 된 모든 스레드가 깨어날 것임을 의미합니다. 스레드가 깨어 난 후에는 Breakbarrier 방법을 호출하여 깨어 났는지 확인하기 위해 다음 세 가지 판단을 수행합니다. 그렇다면 예외가 발생합니다. 정상적인 교체 작업으로 깨어 있는지 확인하십시오. 그렇다면 카운터의 값을 반환합니다. 타임 아웃으로 인해 깨어 났는지 확인하십시오. 그렇다면 브레이크 배리어에게 전화를 걸어 울타리를 깨고 예외를 던집니다. 또한 스레드 중 하나가 시간 초과를 기다리기 때문에 종료되면 전체 게임이 끝나고 다른 스레드가 깨어날 것임을 언급해야합니다. 다음은 NextGeneration 메소드 및 브레이크 배리어 방법에 대한 특정 코드를 게시합니다.
// 펜스를 차세대 개인 void NextGeneration ()로 전환합니다 {// 조건 큐 트립의 모든 스레드를 깨우십시오 .SignAlall (); // 카운터 값을 가로 채기가 필요한 스레드 수로 설정합니다. count = 당사자; // 울타리 생성을 재설정 = 새로운 세대 ();} // 현재 울타리를 뒤집습니다. 비공개 void breakbarrier () {// 현재 울타리 상태를 뒤집기로 설정하십시오. // 카운터 값을 가로 채기가 필요한 스레드 수로 설정합니다. count = 당사자; // 모든 스레드를 깨우십시오 Trip.SignalAll ();}우리는 기본적으로 위의 소스 코드를 통해 Cyclicbarrier의 원칙을 설명했습니다. 경마 예제를 통해 사용하는 것에 대해 자세히 알아 보겠습니다.
클래스 말 구현 런닝 가능 {private static int counter = 0; 개인 최종 int id = 카운터 ++; 개인 int strides = 0; 개인 정적 랜덤 랜드 = 새로운 랜덤 (47); 개인 정적 사이클리 바리어 배리어; 공개 말 (Cyclicbarrier b) {배리어 = B; } @override public void run () {try {while (! ride.terrupted ()) {synchronized (this) {// 경마는 strides += rand.nextint (3)를 실행할 때마다 무작위로 여러 단계를 실행합니다. } barrier.await (); }} catch (예외 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 () {리턴 스트라이드; } public String toString () {return "horse" + id + ""; }} 공개 클래스 경마는 실행 가능한 {private static final int finish_line = 75; 개인 정적 목록 <hors> 말 = New ArrayList <Horse> (); 개인 정적 ExecutorService exec = executors.newCachedThreadPool (); @override public void run () {StringBuilder s = new StringBuilder (); // (int i = 0; i <finish_line; i ++) {s.append ( "="); } system.out.println (s); // (Horse Horse : Horses) {System.out.println (Horse.tracks ()); } // (Horse Horse : Horses) {if (horse.getStrides ()> = finish_line) {System.out.println (Horse + "Won!"); exec.shutdownnow (); 반품; }} // 지정된 시간을 다음 라운드로 놓아주십시오 {timeUnit.milliseconds.sleep (200); } catch (InterruptedException e) {System.out.println ( "배리어 액션 수면 중단"); }} public static void main (String [] args) {cyclicbarrier barrier = new Cyclicbarrier (7, New Horserace ()); for (int i = 0; i <7; i ++) {Horse Horse = New Horse (배리어); 말 (말); exec.Execute (말); }}}}이 경마 프로그램은 주로 콘솔에서 각 경마의 현재 트랙을 사용하여 동적 디스플레이 효과를 달성합니다. 전체 경주의 여러 라운드가 있습니다. 각 경마는 무작위로 몇 단계를 겪은 다음 기다려야 할 방법을 호출합니다. 모든 말이 한 라운드를 완료하면 모든 경마의 현재 트랙을 콘솔에 인쇄하기 위해 작업이 수행됩니다. 이런 식으로, 각 경마의 궤도는 각 라운드에서 계속 커지고 있습니다. 경마 중 하나의 궤적이 처음으로 지정된 가치로 증가하면 전체 레이스가 끝나고 경마는 전체 레이스의 승자가됩니다! 프로그램의 실행 결과는 다음과 같습니다.
이 시점에서 우리는 필연적으로 Cyclicbarrier와 CountdownLatch를 비교할 것입니다. 두 클래스 모두 특정 조건에 도달하기 전에 대기하는 스레드 세트를 구현할 수 있습니다. 그들은 내부에 카운터가 있습니다. 카운터 값이 지속적으로 0으로 감소되면 모든 차단 스레드가 깨어납니다. 차이점은 Cyclicbarrier의 카운터가 자체적으로 제어되는 반면 CountdownLatch의 카운터는 사용자가 제어한다는 것입니다. CyclicBarrier에서, 스레드 호출 대기 방법은 자체를 차단할뿐만 아니라 카운터를 1로 줄인다. CountdownLatch에서는 스레드 호출 대기 방법이 카운터 값을 줄이지 않고만 차단됩니다. 또한 CountdownLatch는 한 라운드 만 차단할 수 있지만 Cyclicbarrier는 원형 차단을 구현할 수 있습니다. 일반적으로 Cyclicbarrier를 사용하면 CountdownLatch의 기능을 구현할 수 있지만 그렇지 않으면 할 수는 없습니다. 예를 들어, 위의 경마 경주 프로그램은 Cyclicbarrier를 사용 하여만 구현할 수 있습니다. 요컨대,이 두 범주 간의 유사점과 차이점은 거의 동일합니다. Cyclicbarrier 및 CountdownLatch를 사용하는시기에 대해서는 독자가 여전히 스스로 이해해야합니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.