CountDownLatch  同步倒数计数器

CountDownLatch是一个同步倒数计数器。CountDownLatch允许一个或多个线程等待其他线程完成操作。

CountDownLatch对象内部存有一个整数作为计数器。调用countDown()方法就将计数器减1,当计数到达0时,则所有等待者会停止等待。计数器的操作是原子性的。

CountDownLatch类的常用API

构造方法

CountDownLatch(int count)  构造方法参数指定了计数的次数。

方法

void await()  使当前线程在锁存器倒计数至0之前一直等待,除非线程被中断。

boolean await(long timeout, TimeUnit unit)  使当前线程在锁存器倒计数至0之前一直等待,除非线程被中断或超出了指定的等待时间。

void countDown()  计数减1。当计数为0,则释放所有等待的线程。

long getCount()  返回当前计数。

String toString()  返回标识此锁存器及其状态的字符串。

用给定的计数初始化 CountDownLatch实例。每调用一次countDown()方法,计数器减1。计数器大于0 时,await()方法会阻塞其他线程继续执行。 利用该特性,可以让主线程等待子线程的结束。

需要注意的是,一旦CountDownLatch的计数到0,则无法再将该计数无法被重置。

一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检查。只有等所有检查完毕后,引擎才能点火。这种场景就非常适合使用CountDownLatch。它可以使得点火线程,等待所有检查线程全部完工后,再执行。

例:有三个工人在为老板干活。老板有一个习惯,当三个工人把一天的活都干完了的时候,他就来检查所有工人所干的活。如下代码设计两个类,Worker代表工人,Boss代表老板。

  1. import java.util.Random;
  2. import java.util.concurrent.CountDownLatch;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. import java.util.concurrent.TimeUnit;
  6.  
  7. public class CountDownLatchDemo {
  8. public static void main(String[] args) {
  9. ExecutorService executor = Executors.newCachedThreadPool();
  10. CountDownLatch latch = new CountDownLatch(3); // 同步倒数计数器。
  11.  
  12. Worker w1 = new Worker(latch, "张三");
  13. Worker w2 = new Worker(latch, "李四");
  14. Worker w3 = new Worker(latch, "王五");
  15. Boss boss = new Boss(latch);
  16.  
  17. executor.execute(w3); // 工人工作。
  18. executor.execute(w2);
  19. executor.execute(w1);
  20. executor.execute(boss); // 老板工作。
  21.  
  22. executor.shutdown();
  23. }
  24.  
  25. }
  26.  
  27. class Worker implements Runnable {
  28. private CountDownLatch downLatch;
  29. private String name;
  30.  
  31. public Worker(CountDownLatch downLatch, String name) {
  32. this.downLatch = downLatch;
  33. this.name = name;
  34. }
  35.  
  36. public void run() {
  37. this.doWork(); // 工人工作。
  38.  
  39. try {
  40. TimeUnit.SECONDS.sleep(new Random().nextInt(10)); // 工作时长。
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44.  
  45. System.out.println(this.name + "活干完了!");
  46. this.downLatch.countDown(); // 计数减1。
  47. }
  48.  
  49. private void doWork() {
  50. System.out.println(this.name + "正在干活!");
  51. }
  52.  
  53. }
  54.  
  55. class Boss implements Runnable {
  56. private CountDownLatch downLatch;
  57.  
  58. public Boss(CountDownLatch downLatch) {
  59. this.downLatch = downLatch;
  60. }
  61.  
  62. public void run() {
  63. System.out.println("老板正在等所有的工人干完活......");
  64. try {
  65. this.downLatch.await(); // 当计数不为0时,线程永远阻塞。为0则继续执行。
  66. } catch (InterruptedException e) {
  67. e.printStackTrace();
  68. }
  69.  
  70. System.out.println("工人活都干完了,老板开始检查了!");
  71. }
  72.  
  73. }

CountDownLatch类与join方法

CountDownLatch实例本质与Thread的join方法相同。但join方法仅可以支持当前线程等待一个线程的结束,若需要等待多个线程,则需要逐个线程的调用join方法,非常麻烦。CountDwonLatch可以很方便的实现一个线程等待多个线程。

CyclicBarrier 循环屏障

CyclicBarrier用于让一组线程运行并互相等待,直到共同到达一个公共屏障点 (common barrier point,又被称为同步点),被屏障拦截的所有线程就会继续执行。

CyclicBarrier与CountDownLatch的功能非常类似。但一个CyclicBarrier实例在释放等待线程后可以继续使用。让下一批线程在屏障点等待。但CountDownLatch实例只能被使用一次。所以CyclicBarrier被称为循环 的 barrier。

典型的比如公司的人员利用集体郊游,先各自从家出发到公司集合,再同时出发游玩,在指定地点集合。CyclicBarrier表示大家彼此在某处等待,集合好后才开始出发,分散活动后又在指定地点集合碰面。

CyclicBarrier类API

构造器

CyclicBarrier(int parties) 创建CyclicBarrier对象,parties 表示屏障拦截的线程数量。

CyclicBarrier(int parties, Runnable barrierAction) 创建 CyclicBarrier对象,该构造方法提供了一个Runnable 参数,在一组线程中的最后一个线程到达之后,执行Runnable中的程序,再之后释放正在等待的线程。Runnable在屏障点上只运行一次。

方法

int await() 通知CyclicBarrier实例,当前线程已经到达屏障点,然后当前线程将被阻塞。

int await(long timeout, TimeUnit unit)  指定当前线程被阻塞的时间。

int getNumberWaiting() 返回当前在屏障处等待的线程数。

int getParties() 返回CyclicBarrier的需要拦截的线程数。

boolean isBroken() 查询此屏障是否处于损坏状态。

void reset() 将屏障重置为其初始状态。

例1:各省数据独立,分库存偖。为了提高计算性能,统计时采用每个省开一个线程先计算单省结果,最后汇总。

  1. import java.util.concurrent.BrokenBarrierException;
  2. import java.util.concurrent.CyclicBarrier;
  3.  
  4. public class Total {
  5.  
  6. public static void main(String[] args) {
  7. TotalService totalService = new TotalServiceImpl();
  8. CyclicBarrier barrier = new CyclicBarrier(5, new TotalTask(totalService));
  9.  
  10. // 实际系统是查出所有省编码code的列表,然后循环,每个code生成一个线程。
  11. new BillTask(new BillServiceImpl(), barrier, "北京").start();
  12. new BillTask(new BillServiceImpl(), barrier, "上海").start();
  13. new BillTask(new BillServiceImpl(), barrier, "广西").start();
  14. new BillTask(new BillServiceImpl(), barrier, "四川").start();
  15. new BillTask(new BillServiceImpl(), barrier, "黑龙江").start();
  16. }
  17.  
  18. }
  19.  
  20. /**
  21. * 主任务:汇总任务
  22. */
  23. class TotalTask implements Runnable {
  24. private TotalService totalService;
  25.  
  26. TotalTask(TotalService totalService) {
  27. this.totalService = totalService;
  28. }
  29.  
  30. public void run() {
  31. // 读取内存中各省的数据汇总,过程略。
  32. totalService.count();
  33. System.out.println("开始全国汇总");
  34. }
  35.  
  36. }
  37.  
  38. /**
  39. * 子任务:计费任务
  40. */
  41. class BillTask extends Thread {
  42. private BillService billService; // 计费服务
  43. private CyclicBarrier barrier;
  44. private String code; // 代码,按省代码分类,各省数据库独立。
  45.  
  46. BillTask(BillService billService, CyclicBarrier barrier, String code) {
  47. this.billService = billService;
  48. this.barrier = barrier;
  49. this.code = code;
  50. }
  51.  
  52. public void run() {
  53. System.out.println("开始计算--" + code + "省--数据!");
  54. billService.bill(code);
  55.  
  56. // 把bill方法结果存入内存,如ConcurrentHashMap,vector等,代码略
  57. System.out.println(code + "省已经计算完成,并通知汇总Service!");
  58. try {
  59. // 通知barrier已经完成
  60. barrier.await();
  61. } catch (InterruptedException e) {
  62. e.printStackTrace();
  63. } catch (BrokenBarrierException e) {
  64. e.printStackTrace();
  65. }
  66. }
  67. }
  68.  
  69. interface BillService {
  70. public void bill(String code);
  71. }
  72.  
  73. interface TotalService {
  74. public void count();
  75. }
  76.  
  77. class BillServiceImpl implements BillService{
  78.  
  79. @Override
  80. public void bill(String code) {}
  81. }
  82.  
  83. class TotalServiceImpl implements TotalService{
  84.  
  85. @Override
  86. public void count(){}
  87. }

例2:赛跑时,等待所有人都准备好时,才起跑。

  1. public class CyclicBarrierTest {
  2.  
  3. public static void main(String[] args) throws IOException, InterruptedException {
  4. CyclicBarrier barrier = new CyclicBarrier(3);
  5. ExecutorService executor = Executors.newFixedThreadPool(3);
  6.  
  7. executor.submit(new Thread(new Runner(barrier, "1号选手")));
  8. executor.submit(new Thread(new Runner(barrier, "2号选手")));
  9. executor.submit(new Thread(new Runner(barrier, "3号选手")));
  10.  
  11. executor.shutdown();
  12. }
  13. }
  14.  
  15. class Runner implements Runnable {
  16. // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
  17. private CyclicBarrier barrier;
  18. private String name;
  19.  
  20. public Runner(CyclicBarrier barrier, String name) {
  21. super();
  22. this.barrier = barrier;
  23. this.name = name;
  24. }
  25.  
  26. @Override
  27. public void run() {
  28. try {
  29. Thread.sleep(1000 * (new Random()).nextInt(8));
  30. System.out.println(name + " 准备好了...");
  31. // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
  32. barrier.await();
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. } catch (BrokenBarrierException e) {
  36. e.printStackTrace();
  37. }
  38. System.out.println(name + " 起跑!");
  39. }
  40.  
  41. }

例3:JDK6中的示例用法:下面是一个在并行分解设计中使用 barrier 的例子。给出示意代码结构,不可运行。

  1. class Solver {
  2. final int N;
  3. final float[][] data;
  4. final CyclicBarrier barrier;
  5.  
  6. class Worker implements Runnable {
  7. int myRow;
  8.  
  9. public Worker(int row) {
  10. myRow = row;
  11. }
  12.  
  13. public void run() {
  14. while (!done()) {
  15. processRow(myRow);
  16. try {
  17. barrier.await();
  18. } catch (InterruptedException ex) {
  19. return;
  20. } catch (BrokenBarrierException ex) {
  21. return;
  22. }
  23. }
  24. }
  25.  
  26. }
  27.  
  28. public Solver(float[][] matrix) {
  29. data = matrix;
  30. N = matrix.length;
  31.  
  32. barrier = new CyclicBarrier(N, new Runnable() {
  33. public void run() {
  34. mergeRows(...);
  35. }
  36. });
  37.  
  38. for (int i = 0; i < N; ++i){
  39. new Thread(new Worker(i)).start();
  40. }
  41.  
  42. waitUntilDone();
  43. }
  44.  
  45. }

在这个例子中,每个 worker 线程处理矩阵的一行,在处理完所有的行之前,该线程将一直在屏障处等待。处理完所有的行之后,将执行所提供的 Runnable 屏障操作,并合并这些行。如果合并者确定已经找到了一个解决方案,那么 done() 将返回 true,所有的 worker 线程都将终止。

如果屏障操作在执行时不依赖于正挂起的线程,则线程组中的任何线程在获得释放时都能执行该操作。为方便此操作,每次调用 await() 都将返回能到达屏障处的线程的索引。然后,可以选择哪个线程应该执行屏障操作,例如:

  1. if (barrier.await() == 0) {
  2. // log the completion of this iteration
  3. }

对于失败的同步尝试,CyclicBarrier 使用了一种要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么在该屏障点等待的其他所有线程也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。

Semaphore信号量

Semaphore用于控制并发线程数。Semaphore实例可以控制当前访问自身的线程个数。使用Semaphore可以控制同时访问资源的线程个数。例如,实现一个文件允许的并发访问数。

Semaphore维护了一个许可集。“许可”即线程进入临界区的许可。一个临界区可以有多个许可。获取许可的线程即可进入。通过 acquire() 获取一个许可,如果线程没有获取到就等待,而 release() 表示释放一个许可。可以把Semaphore看成是一种共享锁。Semaphore允许同一时间多个线程同时访问临界区。

生活的理解:Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。

Semaphore对象也可以实现互斥锁的功能,并且可以是由一个线程获得了"锁",再由另一个线程释放"锁",这可应用于死锁恢复的一些场合。

在一些企业系统中,开发人员经常需要限制未处理的特定资源请求(线程/操作)数量,事实上,限制有时候能够提高系统的吞吐量,因为它们减少了对特定资源的争用。尽管完全可以手动编写限制代码,但使用 Semaphore类可以更轻松地完成此任务,它将帮您执行限制。

常用API

public void acquire()                           // 获取许可。

public void acquireUninterruptibly()

public boolean tryAcquire()

public boolean tryAcquire(long timeout, TimeUnit unit)

public void release()                           // 释放许可。该方法一般调用于finally块中。

例:10 个线程都在运行,可以对运行SemaphoreApp的Java进程执行jstack来验证,只有3个线程是活跃的。在一个信号计数器释放之前,其他7个线程都处于空闲状态。

  1. import java.util.Random;
  2. import java.util.concurrent.Semaphore;
  3.  
  4. public class SemaphoreApp {
  5.  
  6. public static void main(String[] args) {
  7.  
  8. // 匿名Runnable实例。定义线程运行程序。
  9. Runnable limitedCall = new Runnable() {
  10. final Random rand = new Random();
  11. final Semaphore available = new Semaphore(3); // 最多可以发出3个"许可"
  12. int count = 0;
  13.  
  14. public void run() {
  15. int time = rand.nextInt(15);
  16. int num = count++;
  17.  
  18. try {
  19. available.acquire(); // 当前线程获取"许可"。若没有获取许可,则等待于此。
  20. System.out.println("Executing " + "long-running action for " + time + " seconds... #" + num);
  21. Thread.sleep(time * 1000);
  22. System.out.println("Done with #" + num + "!");
  23. } catch (InterruptedException intEx) {
  24. intEx.printStackTrace();
  25. } finally {
  26. available.release(); // 当前线程释放"许可"
  27. }
  28. }
  29. };
  30.  
  31. for (int i = 0; i < 10; i++) {
  32. new Thread(limitedCall).start();
  33. }
  34. }

例:停车示例。停车场只有10个车位,现在有30辆车去停车。当车位满时出来一辆车才能有一辆车进入停车。

  1. import java.util.concurrent.Semaphore;
  2.  
  3. public class Car implements Runnable {
  4. private final Semaphore parkingSlot;
  5. private int carNo;
  6.  
  7. public Car(Semaphore parkingSlot, int carNo) {
  8. this.parkingSlot = parkingSlot;
  9. this.carNo = carNo;
  10. }
  11.  
  12. public void run() {
  13. try {
  14. parkingSlot.acquire(); // 车尝试获取"车位"
  15. parking();
  16. sleep(300);
  17. leaving();
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. } finally {
  21. parkingSlot.release(); // 释放"车位"
  22. }
  23. }
  24.  
  25. private void parking() {
  26. System.out.println(String.format("%d号车泊车", carNo));
  27. }
  28.  
  29. private void leaving() {
  30. System.out.println(String.format("%d号车离开车位", carNo));
  31. }
  32.  
  33. private static void sleep(long millis) {
  34. try {
  35. Thread.sleep(millis);
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40.  
  41. }
  42.  
  43. // --------------------------------------------------------------------------
  44. import java.util.concurrent.ExecutorService;
  45. import java.util.concurrent.Executors;
  46. import java.util.concurrent.Semaphore;
  47.  
  48. public class ParkingCars {
  49. private static final int NUMBER_OF_CARS = 30;
  50. private static final int NUMBER_OF_PARKING_SLOT = 10;
  51.  
  52. public static void main(String[] args) {
  53. Semaphore parkingSlot = new Semaphore(NUMBER_OF_PARKING_SLOT, true); // "车位",采用FIFO,设置true。
  54.  
  55. ExecutorService service = Executors.newCachedThreadPool(); // 创建线程池。模拟30辆车"停车"。
  56. for (int carNo = 1; carNo <= NUMBER_OF_CARS; carNo++) {
  57. service.execute(new Car(parkingSlot, carNo));
  58. }
  59.  
  60. sleep(3000);
  61. service.shutdown(); // 关闭线程池。
  62.  
  63. // 输出剩余可以用的资源数。
  64. System.out.println(parkingSlot.availablePermits() + " 个停车位可以用!");
  65. }
  66.  
  67. private static void sleep(long millis) {
  68. try {
  69. Thread.sleep(millis);
  70. } catch (InterruptedException e) {
  71. e.printStackTrace();
  72. }
  73. }
  74.  
  75. }

Exchanger 交换器

Exchanger用于实现线程间的数据交换。Exchanger提供一个同步点,在同步点上,两个线程使用exchange方法交换彼此数据。如果第一个线程先执行exchange方法,则它会等待第二个线程执行exchange方法。当两个线程同时到达同步点时,这两个线程即可以交换数据。交换完毕后,各自进行以后的程序流程。当两个线程通过Exchanger交换数据的时候,这个交换对于两个线程来说是线程安全的。

exchange()方法将本线程的数据作为参数,传递给伙伴线程,并且该方法返回伙伴线程提供的数据。

当在运行不对称的活动时Exchanger很有用,比如当一个线程填充了buffer,另一个线程从buffer中消费数据时,这两个线程可以用Exchanger来交换数据。

Exchanger<V>类的API

构造器

Exchanger() 创建一个新的 Exchanger。

方法

exchange(V x) 等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。

exchange(V x, long timeout, TimeUnit unit) 等待另一个线程到达此交换点(除非当前线程被中断,或者超出了指定的等待时间),然后将给定的对象传送给该线程,同时接收该线程的对象。

例:以下这个程序demo要做的事情就是生产者在交换前生产5个"生产者",然后再与消费者交换5个数据,然后再生产5个"交换后生产者",而消费者要在交换前消费5个"消费者",然后再与生产者交换5个数据,然后再消费5个"交换后消费者"。import java.util.ArrayList;

  1. import java.util.Iterator;
  2. import java.util.List;
  3. import java.util.concurrent.Exchanger;
  4.  
  5. /**
  6. * 两个线程间的数据交换
  7. */
  8. public class ExchangerDemo {
  9. private static final Exchanger<List<String>> ex = new Exchanger<List<String>>();
  10. private static void sleep(long millis){
  11. try {
  12. Thread.sleep(millis);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17.  
  18. /**
  19. * 内部类,数据生成者
  20. */
  21. class DataProducer implements Runnable {
  22. private List<String> list = new ArrayList<String>();
  23.  
  24. public void run() {
  25. System.out.println("生产者开始生产数据");
  26. for (int i = 1; i <= 5; i++) {
  27. System.out.println("生产了第" + i + "个数据,耗时1秒");
  28. list.add("生产者" + i);
  29. sleep(1000);
  30. }
  31. System.out.println("生产数据结束");
  32. System.out.println("开始与消费者交换数据");
  33.  
  34. try {
  35. //将数据准备用于交换,并返回消费者的数据
  36. list = (List<String>) ex.exchange(list);
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40.  
  41. System.out.println("结束与消费者交换数据");
  42. System.out.println("生产者与消费者交换数据后,再生产数据");
  43. for (int i = 6; i < 10; i++) {
  44. System.out.println("交换后生产了第" + i + "个数据,耗时1秒");
  45. list.add("交换后生产者" + i);
  46. sleep(1000);
  47. }
  48.  
  49. System.out.println("遍历生产者交换后的数据");
  50. for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
  51. System.out.println(iterator.next());
  52. }
  53. }
  54.  
  55. }
  56.  
  57. /**
  58. * 内部类,数据消费者
  59. */
  60. class DataConsumer implements Runnable {
  61. private List<String> list = new ArrayList<String>();
  62.  
  63. public void run() {
  64. System.out.println("消费者开始消费数据");
  65. for (int i = 1; i <= 5; i++) {
  66. System.out.println("消费了第" + i + "个数据");
  67. // 消费者产生数据,后面交换的时候给生产者
  68. list.add("消费者" + i);
  69. }
  70.  
  71. System.out.println("消费数据结束");
  72. System.out.println("开始与生产者交换数据");
  73. try {
  74. // 进行数据交换,返回生产者的数据
  75. list = (List<String>) ex.exchange(list);
  76. } catch (InterruptedException e) {
  77. e.printStackTrace();
  78. }
  79.  
  80. System.out.println("消费者与生产者交换数据后,再消费数据");
  81. for (int i = 6; i < 10; i++) {
  82. System.out.println("交换后消费了第" + i + "个数据");
  83. list.add("交换后消费者" + i);
  84. sleep(1000);
  85. }
  86. sleep(1000);
  87. System.out.println("开始遍历消费者交换后的数据");
  88.  
  89. for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
  90. System.out.println(iterator.next());
  91. }
  92. }
  93. }
  94.  
  95. // 主方法
  96. public static void main(String args[]) {
  97. ExchangerDemo et = new ExchangerDemo();
  98. new Thread(et.new DataProducer()).start();
  99. new Thread(et.new DataConsumer()).start();
  100. }
  101. }

Java并发——同步工具类的更多相关文章

  1. Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo

    Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo CountDownLatch countDownLatch这个类使一个线程等待其他线程 ...

  2. java 利用同步工具类控制线程

    前言 参考来源:<java并发编程实战> 同步工具类:根据工具类的自身状态来协调线程的控制流.通过同步工具类,来协调线程之间的行为. 可见性:在多线程环境下,当某个属性被其他线程修改后,其 ...

  3. Java多线程同步工具类之CountDownLatch

    在过去我们实现多线程同步的代码中,往往使用join().wait().notiyAll()等线程间通信的方式,随着JUC包的不断的完善,java为我们提供了丰富同步工具类,官方也鼓励我们使用工具类来实 ...

  4. java并发编程工具类JUC第八篇:ConcurrentHashMap

    在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...

  5. java并发编程工具类JUC第四篇:LinkedBlockingQueue链表队列

    在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue. LinkedBlockingQueue 队列是Blo ...

  6. java并发编程工具类JUC第七篇:BlockingDeque双端阻塞队列

    在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...

  7. Java多线程同步工具类之Semaphore

    Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数. 一.Semaphore使用 下面我们通过一个简单的例子来看下Semaphore的具体使用,我们 ...

  8. java并发编程工具类辅助类:CountDownLatch、CyclicBarrier和 Semaphore

    在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法. 以下 ...

  9. Java 并发同步工具(转)

    转自:https://www.jianshu.com/p/e80043ac4115 在 java 1.5 中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如 CountDownLatch,Cy ...

随机推荐

  1. Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces

    问题解决:缺少jar包 cglib-2.1.3.jar

  2. poj2187

    求最远点对,这是一道经典的旋转卡壳的题目话说由于是前年写的,之后就没怎么研究过计算几何了……感觉都不大记得清了,来稍微回忆一下……首先最远点对一定出现在凸包上显然,然后穷举肯定不行,这时候就需要旋转卡 ...

  3. diamond专题(四)—— 容灾机制

    大家好,本次为大家带来diamond的容灾机制. diamond之所以表现的稳定可靠,除了架构简单之外,另一个重要原因是diamond具有一套完备的容灾机制,容灾机制涉及到client和server两 ...

  4. @DataProvider ITestContext 参数

    package roger.testng; import java.util.Random; import org.testng.ITestContext; import org.testng.ann ...

  5. selenium2.0 处理各种窗口问题解决方法

    selenium2.0处理muti-Windows . Frames .Popup Dialogs selenium2.0处理多窗口,弹窗等,只需要调用WebDriver 嵌套类:TargetLoca ...

  6. GTK+中的树状列表构件(GtkTreeView)

    GTK+中的树状列表构件(GtkTreeView) GTK+中的树状列表构件(GtkTreeView) 在本章的GTK+程序设计教程中,我们将向大家重点介绍非常常用也有点复杂的构件--GtkTreeV ...

  7. 支持IE6以上阴影效果纯CSS

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. 【原】日志处理-Spark

    日志信息如下所示: 1.1.1.1 - - [21/Jul/2014:10:00:00 -0800] "GET /majihua/article/284234 HTTP/1.1"  ...

  9. oracle 对象上锁,不能插入或删除情况

    ora-00054:resource busy and acquire with nowait specified解决方法 当某个数据库用户在数据库中插入.更新.删除一个表的数据,或者增加一个表的主键 ...

  10. Centos添加硬盘分区、挂载磁盘

    默认一个硬盘,新增硬盘识别为sdb.sdbc一次类推 1.查看磁盘信息 >fdisk -l #新磁盘为/dev/sdb,截图是已经分区完成了. 2.进入分区界面 >fdisk /dev/s ...