在java并发包中提供了线程池相关的API
主要有Executor、ExectorService接口,ThreadPoolExecutor、ScheduledThreadPoolExecutor类,Executors工具类,CompletionService接口、ExecutorCompletionService类,Callable、Future接口、FutureTask类等。以下简要画了个UML图:
线程池的基本思想:线程频繁的创建、销毁会极大地占用系统资源,为了减少系统在创建销毁线程时的开销,线程池应运而生。线程池包括多个已创建的线程,当有任务要在新线程中执行时,将任务提交给线程池,线程池选取空闲线程或新开线程执行该任务,可见线程池应维护一个任务队列和线程队列。此外还要对线程最大数、最小数目、空闲等待时间等进行管理。
线程池的组成(百度百科)
并发包中提供了几个用于线程同步的类
1、CountDownLatch
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
CountDownLatch 是一个通用同步工具,它有很多用途。
将计数 1 初始化的 CountDownLatch
用作一个简单的开/关锁存器,或入口:在通过调用 countDown()
的线程打开入口前,所有调用 await
的线程都一直在入口处等待。
用 N 初始化的 CountDownLatch
可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。(API文档)
await会造成一个线程的阻塞,直到countdown到0;文档中说CountDownLatch 等待的是事件,也就是countdown到0,即之前的所有所需操作完成。
2、CyclicBarrier
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。
多个线程共同工作,一个常用的比方是:大家一起出游,分别到达集合地点,共同去往目的地;到达目的地,分散活动,午餐时间集合;餐后分散,结束后集合回程。这种先分别执行,达到某一时间点,线程间相互等待,共同执行后,再分散,在集中的情形。
以上二者的区别:
CountDownLatch适用于有明确先后顺序的两组操作,前一组完成后开启后一组,开启功能完成(countdown到0),失去作用;await使线程等待,结束等待条件是countdown到0.
CyclicBarrier适用于多个线程之间需要相互等待,即线程的任务间有分-合-分-合的关系,只有个线程都达到条件后才能进行下一步操作(合),CyclicBarrier可以循环使用;await使线程等待,结束等待条件是等待的线程达到要求数目。
3、信号量Semaphore
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。包含一个阻塞队列,信号量维护了一个许可集,在线程访问资源前,先要获取许可,许可通过acquire获取,若许可不可得则线程阻塞。release用于释放许可。流程:获取许可,同步获取资源,释放资源,释放许可。
4、Exchanger
用于线程间成对的交换数据,当一方数据准备好后调用exchange会等待另一方数据准备完成。
每个线程将条目上的某个方法呈现给 exchange
方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。(文档看不太明白)
exchanger参考http://hi.baidu.com/hxzon/item/d13c10deb656d2ef3cc2cb48
阻塞队列 BlockingQueue(文档)
线程安全,程序员不必考虑使用阻塞队列时的线程同步。
BlockingQueue 方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,第二种是返回一个特殊值(null 或 false,具体取决于操作),第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。下表中总结了这些方法:
抛出异常 | 特殊值 | 阻塞 | 超时 | |
插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
检查 | element() | peek() | 不可用 | 不可用 |
1、ArrayBlockingQueue 使用循环数组实现队列,使用一个可重入锁,及其上的两个条件Condition完成同步。
一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
2、LinkedBlockingQueue 使用单链表实现队列,使用两个锁takelock、putlock及其条件实现同步,两个锁是的可以在链表头尾可以同时有线程在操作,一存一取,提高并发性。此外还有fullylock是两个锁的顺序组合,固定顺序可以避免死锁。参见参考博文
3、SynchronousQueue
一种无缓冲的等待队列,类似于无中介的直接交易,有点像原始社会中的生产者和消费者,生产者拿着产品去集市销售给产品的最终消费者,而消费者必须亲自去集市找到所要商品的直接生产者,如果一方没有找到合适的目标,那么对不起,大家都在集市等待。见参考博文
参考资料:锁机制:http://www.blogjava.net/xylz/archive/2010/07/08/325540.html