同步工具:CountDownLatch、CyclicBarrier和Semaphore

1. CountDownLatch

1.1 功能及使用场景

一个同步工具,使得一个或多个线程等待一组线程执行完成后再执行。

使用场景:等待一些前置任务执行完成后,再执行特定的功能。比如,系统启动时,各种配置生效后,才能运行提供服务。

1.2 代码实例

public class CountDownLatchTest {

    public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(5); for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end, " + System.currentTimeMillis());
latch.countDown();
}
}).start();
} try {
System.out.println("main latch.await() " + System.currentTimeMillis());
latch.await();
System.out.println("main latch.await() end " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} } }

特别注意:CountDownLatch初始化时,数量一定要等于等待的线程的数量。

2. CyclicBarrier

2.1 功能及使用场景

CyclicBarrier(可循环同步屏障),控制一组线程相互等待,直到所有线程都到达屏障点。所有线程都到达屏障点后,同时开始执行剩余的任务。

可循环,意味着当所有线程都到达屏障后,屏障可以被再次重复利用。

使用场景

多个任务同时到达某个临界情况时,才能同时执行剩余任务,否则相互等待。

2.2 示例代码

public class CyclicBarrierTest {

    public static void main(String[] args) {
int num = 3;
final CyclicBarrier barrier = new CyclicBarrier(num);
for (int i = 0; i < num; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + "执行结束,开始等待其它线程, " + System.currentTimeMillis());
barrier.await();
System.out.println(Thread.currentThread().getName() + "所有线程等待都执行完成,开始执行剩下任务, " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
} // 屏障可以在上一次使用完成后被再次使用
for (int i = 0; i < num; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + "执行结束,开始等待其它线程, " + System.currentTimeMillis());
barrier.await();
System.out.println(Thread.currentThread().getName() + "所有线程等待都执行完成,开始执行剩下任务, " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
}
System.out.println("main end at " + System.currentTimeMillis());
} }

3. Semaphore

3.1 功能及使用场景

Semaphore(信号量),一个计数信号量。概念上,一个信号量维持了一个许可集合。

  • acquire()方法:获取一个许可,如果没有获取到许可,则一直阻塞到获得一个许可;
  • release()方法:归还一个许可。

实际上,并没有真正的许可对象,只是计数。

使用场景

限制资源同时被访问的线程数量

3.2 使用代码

public class SemaphoreTest {

    public static void main(String[] args) {
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "尝试获取许可, " + System.currentTimeMillis());
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "获取许可, " + System.currentTimeMillis());
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + "执行结束, " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}).start();
}
System.out.println("main end! " + System.currentTimeMillis());
} }

特别注意:获得许可并执行完逻辑后,一定要释放,否则许可不会被归还。(有借有还,再借不难)

4. 总结

  • CountDownLatch:一个任务等待前置任务执行完后才能执行
  • CyclicBarrier: 一组任务相互等待,直到所有线程均到达某个临界状态
  • Semaphore: 一组许可,控制资源被同时访问的数量(获取许可,也要归还许可)
上一篇:关于Dropdownlist使用的心得体会


下一篇:Linux 命令 - sort: 行排序文本文件