Java并发编程之2——同步工具类 CountDownLatch,CyclicBarrier,BlockungQueue,Semaphore
为了简化线程同步与互斥的相关操作JDK,提供了大约4中同步与互斥的工具类: 闭锁(CountDownLatch),栅栏(CyclicBarrier),阻塞队列(BlockingQueue),信号量(semaphore)。本文将对比分析四种同步工具类的使用范例,和应用场景。
闭锁(CountDownLatch)
闭锁的作用主要是允许一个或者多个线程等待某个事件的完成,当初始化一个CountDownLatch的时候需要指定同步计数器的个数(等待线程的个数),这个时候主线程的wait方法会一直阻塞,子线程每调用一次countDown方法,同步计数器就会减1,当这个值减少到0时,主线程的wait方法处就会解除阻塞。
CountDownLatch的主要方法如下:
- public CountDownLatch(int count); //构造函数 传入一个同步计数器 当同步计数器大于0的时候主线程调用wait的地方会一直阻塞
- public void countDown(); //当子线程调用一次countDown方法的时候同步计数器的数值就会减1
- public void await() throws InterruptedException //当同步计数器的数值大于0的时候,主线程会一直在wait方法处等待,当同步计数器的大小为0的时候,主线程就会解除阻塞继续运行。
CountDownLatch示例:
CountDownLatch的适用场景:
主线程在做一项工作之前需要一系列准备工作,只有这些准备工作都完成之后,主线程才能继续他的工作,而这些准备工作之间是相互独立的。例如 我们的搜索系统,在启动是时候主要有两部分的工作:
- 准备数据
- 建立数据之间的关联关系
在建立数据关系之前数据的准备必须完成,而大部分的数据之间是相互独立的,所以可以分一些子任务并发运行,当所有的子任务运行完成之后,就可以建立数据关系
栅栏(CyclicBarrier)
栅栏的主要作用是等待其他线程的完成,他CountDownLatch的主要区别是:
- CountDownLatch主要是等待事件的完成,每个子线程只能countDown()一次,当子线程调用一次countDown方法,就表示当前线程的事件完成。(子线程之间不是相互等待,子线程完成之后在主线程的wait处等待)
- CyclicBarrier主要是等待其他的线程完成(所有的子线程之间会相互等待,主线程的流程不会受到子线程的影响)。他的主要方法是一个await(),这个方法每被调用一次计数器便会减1,并阻塞住当前的线程,当计数减到0的时候,阻塞会解除,在这个栅栏上面等待的所有线程都会继续运行。然后进行一次新的循环(这时如果是CountDownLatch会回到主线程运行,而不是唤醒所有在栅栏上面等待的子线程继续运行)
CyclicBarrier的主要方法: - await() 这个方法每被调用一次计数器便会减1,并阻塞住当前的线程,当计数减到0的时候,阻塞会解除,在这个栅栏上面等待的所有线程都会继续运行。然后进行一次新的循环。
CyclicBarrier示例:
阻塞队列(BlockingQueue)
BlockingQueue是Java提供的一种阻塞队列实现方式,他主要有四种实现:
- ArrayBlockingQueue 有界的阻塞队列,在实例化的时候需要指明有界队列的大小,队列采用先进先出的策略
- LinkedBlockQueue 有两种构造方法默认的是不传递任何参数,这种方式是一个无界的阻塞队列,另一种方式和ArrayBlockingQueue相同
- SynchronousBlockQueue 比较特殊(所有的读写操作都是读取交替运行才可以)
- PriorityBolckQueue 有优先级的阻塞队列
阻塞队列的主要方法(以ArrayBlockingQueue为列子说明): - piblic ArrayBlockQueue(int count) 构造方法制定阻塞队列的大小
- piblic void put() 方法在队列满的时候会阻塞直到有队列成员被消费,
- public T take() 方法在队列空的时候会阻塞,直到有队列成员被放进来
BlockingQueue示例:
信号量(Semaphore)
信号量通过构造函数设定一个数量的许可(permit),然后通过acquire获取许可,通过release释放许可,当acquire获取到许可之后当前的线程就可以执行,否则当前线程阻塞。Semaphore通常用于限制可以访问资源的数量,例如数据库连接池,线程池。单个信号量的semaphore可以用来实现互斥锁
Semaphore的主要方法如下:
- Semaphore(int permits, boolean fair) //创建具有给定的许可数和给定的公平设置的Semaphore。 如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部
- acquire() 获取一个许可,如果没有就等待,当前线程阻塞直到获取到许可之后才会继续执行
- release() 释放一个许可。
Semaphore示例: