Java 1.5에서는 동시 프로그래밍에 도움이 되는 CountDownLatch, CyclicBarrier 및 Semaphore와 같은 매우 유용한 보조 클래스가 제공됩니다. 오늘은 이 세 가지 보조 클래스의 사용법을 알아 보겠습니다.
이 글의 목차는 다음과 같습니다.
1. CountDownLatch 사용법
2. CyclicBarrier 사용법
3.
1. CountDownLatch 사용법
CountDownLatch 클래스는 java.util.concurrent 패키지 아래에 위치하며 이를 사용하여 카운터와 유사한 기능을 구현할 수 있습니다. 예를 들어, 실행되기 전에 다른 4개의 작업이 완료될 때까지 기다려야 하는 작업 A가 있습니다. 이때 CountDownLatch를 사용하여 이 기능을 구현할 수 있습니다.
CountDownLatch 클래스는 하나의 생성자만 제공합니다.
public CountDownLatch(int count) { }; //参数count为计数值
다음 세 가지 메서드는 CountDownLatch 클래스에서 가장 중요한 메서드입니다.
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行 public void countDown() { }; //将count值减1
아래를 참조하세요. 예를 들어, 모두가 CountDownLatch의 사용법을 알게 될 것입니다:
public class Test { public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(2); new Thread(){ public void run() { try { System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); Thread.sleep(3000); System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); new Thread(){ public void run() { try { System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); Thread.sleep(3000); System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); try { System.out.println("等待2个子线程执行完毕..."); latch.await(); System.out.println("2个子线程已经执行完毕"); System.out.println("继续执行主线程"); } catch (InterruptedException e) { e.printStackTrace(); } } }
실행 결과:
线程Thread-0正在执行 线程Thread-1正在执行 等待2个子线程执行完毕... 线程Thread-0执行完毕 线程Thread-1执行完毕 2个子线程已经执行完毕 继续执行主线程
말 그대로 루프 펜스를 의미하며, 이를 통해 스레드 그룹이 특정 상태까지 기다릴 수 있습니다. 동시에 모두 실행하십시오. 모든 대기 스레드가 해제된 후에 CyclicBarrier를 재사용할 수 있기 때문에 이를 루프백이라고 합니다. 일단 이 상태 장벽을 호출해 보겠습니다. wait() 메서드가 호출되면 스레드는 장벽에 있게 됩니다.
CyclicBarrier 클래스는 java.util.concurrent 패키지 아래에 있습니다. CyclicBarrier는 2개의 생성자를 제공합니다.
public CyclicBarrier(int parties, Runnable barrierAction) { } public CyclicBarrier(int parties) { }
매개변수 당사자는 대기할 수 있는 스레드 또는 작업 수를 나타냅니다. Barrier 상태, BarrierAction 매개변수는 이러한 스레드가 Barrier 상태에 도달할 때 실행되는 것입니다.
CyclicBarrier에서 가장 중요한 메소드는 2개의 오버로드된 버전이 있는 wait 메소드입니다.
public int await() throws InterruptedException, BrokenBarrierException { }; public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { }
첫 번째 버전은 더 일반적으로 사용되며 모든 스레드가 종료될 때까지 현재 스레드를 일시 중단하는 데 사용됩니다. 스레드는 모두 장벽 상태에 도달한 후 동시에 후속 작업을 수행합니다.
두 번째 버전은 이러한 스레드가 여전히 장벽에 도달하지 못한 경우 일정 시간 동안 대기하도록 하는 것입니다. 상태에서는 장벽에 도달한 스레드가 후속 작업을 직접 수행하도록 합니다.
몇 가지 예를 보면 이해하는 데 도움이 됩니다.
데이터를 써야 하는 스레드가 여러 개 있고 모든 스레드가 데이터 쓰기 작업을 완료한 후에만 이러한 스레드가 계속해서 데이터 쓰기 작업을 수행할 수 있다고 가정해 보겠습니다. 다음과 같이 이때 CyclicBarrier를 사용할 수 있습니다.
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i<N;i++) new Writer(barrier).start(); } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据..."); try { Thread.sleep(5000); //以睡眠来模拟写入数据操作 System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println("所有线程写入完毕,继续处理其他任务..."); } } }
실행 결과:
线程Thread-0正在写入数据... 线程Thread-3正在写入数据... 线程Thread-2正在写入数据... 线程Thread-1正在写入数据... 线程Thread-2写入数据完毕,等待其他线程写入完毕 线程Thread-0写入数据完毕,等待其他线程写入完毕 线程Thread-3写入数据完毕,等待其他线程写入完毕 线程Thread-1写入数据完毕,等待其他线程写入完毕 所有线程写入完毕,继续处理其他任务... 所有线程写入完毕,继续处理其他任务... 所有线程写入完毕,继续处理其他任务... 所有线程写入完毕,继续处理其他任务...
위 출력 결과에서 볼 수 있듯이 각 쓰기 스레드는 데이터 쓰기 작업을 완료했습니다. 그 후에는 다른 스레드가 쓰기 작업을 완료할 때까지 기다립니다.
모든 스레드가 쓰기 작업을 완료하면 모든 스레드는 계속해서 후속 작업을 수행합니다.
모든 스레드가 쓰기 작업을 마친 후 추가 작업을 수행하려는 경우 CyclicBarrier에 Runnable 매개변수를 제공할 수 있습니다.
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N,new Runnable() { @Override public void run() { System.out.println("当前线程"+Thread.currentThread().getName()); } }); for(int i=0;i<N;i++) new Writer(barrier).start(); } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据..."); try { Thread.sleep(5000); //以睡眠来模拟写入数据操作 System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println("所有线程写入完毕,继续处理其他任务..."); } } }
실행 결과:
线程Thread-0正在写入数据... 线程Thread-1正在写入数据... 线程Thread-2正在写入数据... 线程Thread-3正在写入数据... 线程Thread-0写入数据完毕,等待其他线程写入完毕 线程Thread-1写入数据完毕,等待其他线程写入完毕 线程Thread-2写入数据完毕,等待其他线程写入完毕 线程Thread-3写入数据完毕,等待其他线程写入完毕 当前线程Thread-3 所有线程写入完毕,继续处理其他任务... 所有线程写入完毕,继续处理其他任务... 所有线程写入完毕,继续处理其他任务... 所有线程写入完毕,继续处理其他任务...
결과를 보면 4개의 스레드가 모두 Barrier 상태에 도달하면 4개의 스레드 중 하나의 스레드가 선택되어 Runnable을 실행하는 것을 알 수 있습니다.
await 시간을 지정했을 때의 효과를 살펴보겠습니다.
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i<N;i++) { if(i<N-1) new Writer(barrier).start(); else { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } new Writer(barrier).start(); } } } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据..."); try { Thread.sleep(5000); //以睡眠来模拟写入数据操作 System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕"); try { cyclicBarrier.await(2000, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务..."); } } }
실행 결과:
线程Thread-0正在写入数据... 线程Thread-2正在写入数据... 线程Thread-1正在写入数据... 线程Thread-2写入数据完毕,等待其他线程写入完毕 线程Thread-0写入数据完毕,等待其他线程写入完毕 线程Thread-1写入数据完毕,等待其他线程写入完毕 线程Thread-3正在写入数据... java.util.concurrent.TimeoutException Thread-1所有线程写入完毕,继续处理其他任务... Thread-0所有线程写入完毕,继续处理其他任务... at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) java.util.concurrent.BrokenBarrierException at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) java.util.concurrent.BrokenBarrierException at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) Thread-2所有线程写入完毕,继续处理其他任务... java.util.concurrent.BrokenBarrierException 线程Thread-3写入数据完毕,等待其他线程写入完毕 at java.util.concurrent.CyclicBarrier.dowait(Unknown Source) at java.util.concurrent.CyclicBarrier.await(Unknown Source) at com.cxh.test1.Test$Writer.run(Test.java:58) Thread-3所有线程写入完毕,继续处理其他任务...
위 코드는 for에서 의도적으로 사용되었습니다. 기본 메서드의 루프. 처음 세 개의 스레드가 장벽에 도달한 후 지정된 시간 동안 기다렸다가 네 번째 스레드가 장벽에 도달하지 않은 것을 확인한 후 예외가 발생하고 후속 작업이 계속되기 때문에 마지막 스레드가 지연되기 시작합니다. 처형되다.
또한 CyclicBarrier를 재사용할 수 있습니다.
public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i<N;i++) { new Writer(barrier).start(); } try { Thread.sleep(25000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("CyclicBarrier重用"); for(int i=0;i<N;i++) { new Writer(barrier).start(); } } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据..."); try { Thread.sleep(5000); //以睡眠来模拟写入数据操作 System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务..."); } } }
실행 결과:
线程Thread-0正在写入数据... 线程Thread-1正在写入数据... 线程Thread-3正在写入数据... 线程Thread-2正在写入数据... 线程Thread-1写入数据完毕,等待其他线程写入完毕 线程Thread-3写入数据完毕,等待其他线程写入完毕 线程Thread-2写入数据完毕,等待其他线程写入完毕 线程Thread-0写入数据完毕,等待其他线程写入完毕 Thread-0所有线程写入完毕,继续处理其他任务... Thread-3所有线程写入完毕,继续处理其他任务... Thread-1所有线程写入完毕,继续处理其他任务... Thread-2所有线程写入完毕,继续处理其他任务... CyclicBarrier重用 线程Thread-4正在写入数据... 线程Thread-5正在写入数据... 线程Thread-6正在写入数据... 线程Thread-7正在写入数据... 线程Thread-7写入数据完毕,等待其他线程写入完毕 线程Thread-5写入数据完毕,等待其他线程写入完毕 线程Thread-6写入数据完毕,等待其他线程写入完毕 线程Thread-4写入数据完毕,等待其他线程写入完毕 Thread-4所有线程写入完毕,继续处理其他任务... Thread-5所有线程写入完毕,继续处理其他任务... Thread-6所有线程写入完毕,继续处理其他任务... Thread-7所有线程写入完毕,继续处理其他任务...
실행에서 볼 수 있듯이 결과는 처음 4개 스레드가 장벽 상태를 통과한 후 새로운 사용 라운드에 사용될 수 있습니다. CountDownLatch는 재사용할 수 없습니다.
3. 세마포어 사용법
세마포어는 말 그대로 세마포어(Semaphore)로 번역하면 동시에 접근하는 스레드의 수를 제어하고, 획득()을 통해 권한을 얻고, 그렇지 않은 경우 대기합니다. release()는 권한을 해제합니다.
Semaphore 클래스는 java.util.concurrent 패키지 아래에 있습니다.
public Semaphore(int permits) { //参数permits表示许可数目,即同时可以允许多少线程进行访问 sync = new NonfairSync(permits); } public Semaphore(int permits, boolean fair) { //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可 sync = (fair)? new FairSync(permits) : new NonfairSync(permits); }
Semaphore 클래스의 몇 가지 중요한 메서드에 대해 이야기해 보겠습니다. acquire(), release() 메서드:
public void acquire() throws InterruptedException { } //获取一个许可 public void acquire(int permits) throws InterruptedException { } //获取permits个许可 public void release() { } //释放一个许可 public void release(int permits) { } //释放permits个许可
acquire()는 권한을 얻는 데 사용됩니다. 권한을 얻을 수 없으면 권한을 얻을 때까지 기다립니다.
release()는 권한을 해제하는 데 사용됩니다. 공개하려면 먼저 허가를 받아야 합니다.
이 네 가지 방법은 차단됩니다. 실행 결과를 즉시 얻으려면 다음 방법을 사용할 수 있습니다.
public boolean tryAcquire() { }; //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
또한 사용 가능한 수를 얻을 수도 있습니다. availablePermits() 메소드를 통해 권한을 부여합니다.
예제를 통해 세마포어의 구체적인 용도를 살펴보겠습니다.
假若一个工厂有5台机器,但是有8个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。那么我们就可以通过Semaphore来实现:
public class Test { public static void main(String[] args) { int N = 8; //工人数 Semaphore semaphore = new Semaphore(5); //机器数目 for(int i=0;i<N;i++) new Worker(i,semaphore).start(); } static class Worker extends Thread{ private int num; private Semaphore semaphore; public Worker(int num,Semaphore semaphore){ this.num = num; this.semaphore = semaphore; } @Override public void run() { try { semaphore.acquire(); System.out.println("工人"+this.num+"占用一个机器在生产..."); Thread.sleep(2000); System.out.println("工人"+this.num+"释放出机器"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
执行结果:
工人0占用一个机器在生产... 工人1占用一个机器在生产... 工人2占用一个机器在生产... 工人4占用一个机器在生产... 工人5占用一个机器在生产... 工人0释放出机器 工人2释放出机器 工人3占用一个机器在生产... 工人7占用一个机器在生产... 工人4释放出机器 工人5释放出机器 工人1释放出机器 工人6占用一个机器在生产... 工人3释放出机器 工人7释放出机器 工人6释放出机器
下面对上面说的三个辅助类进行一个总结:
1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;
而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。
参考资料:
《Java编程思想》
http://www.itzhai.com/the-introduction-and-use-of-a-countdownlatch.html
http://leaver.me/archives/3220.html
http://developer.51cto.com/art/201403/432095.htm
http://blog.csdn.net/yanhandle/article/details/9016329
http://blog.csdn.net/cutesource/article/details/5780740
http://www.cnblogs.com/whgw/archive/2011/09/29/2195555.html

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

Dreamweaver Mac版
시각적 웹 개발 도구

VSCode Windows 64비트 다운로드
Microsoft에서 출시한 강력한 무료 IDE 편집기

WebStorm Mac 버전
유용한 JavaScript 개발 도구
