Java 并发工具箱之concurrent包
概述
java.util.concurrent 包是专为 Java并发编程
而设计的包。包下的所有类可以分为如下几大类:
- locks部分:显式锁(互斥锁和速写锁)相关;
- atomic部分:原子变量类相关,是构建非阻塞算法的基础;
- executor部分:线程池相关;
- collections部分:并发容器相关;
- tools部分:同步工具相关,如信号量、闭锁、栅栏等功能;
类图结构:
脑图地址: http://www.xmind.net/m/tJy5,感谢深入浅出 Java Concurrency,此脑图在这篇基础上修改而来。
BlockingQueue
此接口是一个线程安全
的 存取实例
的队列。
使用场景
BlockingQueue通常用于一个线程生产对象,而另外一个线程消费这些对象的场景。
注意事项:
- 此队列是
有限的
,如果队列到达临界点,Thread1
就会阻塞,直到Thread2
从队列中拿走一个对象。 - 若果队列是空的,
Thread2
会阻塞,直到Thread1
把一个对象丢进队列。
相关方法
BlockingQueue中包含了如下操作方法:
Throws Exception | Special Value | Blocks | Times Out | |
---|---|---|---|---|
Insert | add(o) | offer(o) | put(o) | offer(o, timeout, timeunit) |
Remove | remove(o) | poll() | take() | poll(timeout, timeunit) |
Examine | element() | peek() |
名词解释:
- Throws Exception: 如果试图的操作无法立即执行,抛一个异常。
- Special Value: 如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
- Blocks: 如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
- Times Out: 如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。
注意事项:
- 无法插入 null,否则会抛出一个 NullPointerException。
- 队列这种数据结构,导致除了获取开始和结尾位置的其他对象的效率都不高,虽然可通过
remove(o)
来移除任一对象。
实现类
因为是一个接口,所以我们必须使用一个实现类来使用它,有如下实现类:
- ArrayBlockingQueue: 数组阻塞队列
- DelayQueue: 延迟队列
- LinkedBlockingQueue: 链阻塞队列
- PriorityBlockingQueue: 具有优先级的阻塞队列
- SynchronousQueue: 同步队列
使用示例:
ArrayBlockingQueue
ArrayBlockingQueue 是一个有界的阻塞队列
- 内部实现是将对象放到一个数组里。数组有个特性:一旦初始化,大小就无法修改。因此无法修改
ArrayBlockingQueue
初始化时的上限。 ArrayBlockingQueue
内部以FIFO(先进先出)
的顺序对元素进行存储。队列中的头元素在所有元素之中是放入时间最久的那个,而尾元素则是最短的那个。
DelayQueue
DelayQueue 对元素进行持有直到一个特定的延迟到期。注入其中的元素必须实现 java.util.concurrent.Delayed 接口:
public interface Delayed extends Comparable<Delayed< {
public long getDelay(TimeUnit timeUnit); // 返回将要延迟的时间段
}
- 1
- 2
- 3
- 在每个元素的 getDelay() 方法返回的值的时间段之后才释放掉该元素。如果返回的是 0 或者负值,延迟将被认为过期,该元素将会在 DelayQueue 的下一次 take 被调用的时候被释放掉。
Delayed
接口也继承了java.lang.Comparable
接口,Delayed
对象之间可以进行对比。这对DelayQueue
队列中的元素进行排序时有用,因此它们可以根据过期时间进行有序释放。
LinkedBlockingQueue
内部以一个链式结构(链接节点)对其元素进行存储 。
- 可以选择一个上限。如果没有定义上限,将使用 Integer.MAX_VALUE 作为上限。
- 内部以 FIFO(先进先出)的顺序对元素进行存储。
PriorityBlockingQueue
一个无界的并发队列,它使用了和类 java.util.PriorityQueue 一样的排序规则。
- 无法向这个队列中插入 null 值。
- 插入到 其中的元素必须实现 java.lang.Comparable 接口。
- 对于具有相等优先级(compare() == 0)的元素并不强制任何特定行为。
- 从一个 PriorityBlockingQueue 获得一个 Iterator 的话,该 Iterator 并不能保证它对元素的遍历是以优先级为序的。
SynchronousQueue
一个特殊的队列,它的内部同时只能够容纳单个元素。
- 如果该队列已有一元素的话,试图向队列中插入一个新元素的线程将会阻塞,直到另一个线程将该元素从队列中抽走。
- 如果该队列为空,试图向队列中抽取一个元素的线程将会阻塞,直到另一个线程向队列中插入了一条新的元素。
BlockingDeque
此接口表示一个线程安全
放入和提取实例的双端队列
。
使用场景
通常用在一个线程既是生产者又是消费者的时候。
注意事项
- 如果双端队列已满,插入线程将被阻塞,直到一个移除线程从该队列中移出了一个元素。
- 如果双端队列为空,移除线程将被阻塞,直到一个插入线程向该队列插入了一个新元素。
相关方法
Throws Exception | Special Value | Blocks | Times Out | |
---|---|---|---|---|
Insert | addFirst(o) | offerFirst(o) | putFirst(o) | offerFirst(o, timeout, timeunit) |
Remove | removeFirst(o) | pollFirst(o) | takeFirst(o) | pollFirst(timeout, timeunit) |
Examine | getFirst(o) | peekFirst(o) |
Throws Exception | Special Value | Blocks | Times Out | |
---|---|---|---|---|
Insert | addLast(o) | offerLast(o) | putLast(o) | offerLast(o, timeout, timeunit) |
Remove | removeLast(o) | pollLast(o) | takeLast(o) | pollLast(timeout, timeunit) |
Examine | getLast(o) | peekLast(o) |
注意事项
- 关于方法的处理方式和上节一样。
- BlockingDeque 接口继承自 BlockingQueue 接口,可以用其中定义的方法。
实现类
- LinkedBlockingDeque : 链阻塞双端队列
LinkedBlockingDeque
LinkedBlockingDeque 是一个双端队列,可以从任意一端插入或者抽取元素的队列。
- 在它为空的时候,一个试图从中抽取数据的线程将会阻塞,无论该线程是试图从哪一端抽取数据。
ConcurrentMap
一个能够对别人的访问(插入和提取)进行并发处理的 java.util.Map接口。
ConcurrentMap 除了从其父接口 java.util.Map 继承来的方法之外还有一些额外的原子性方法。
实现类
因为是接口,必须用实现类来使用它,其实现类为
- ConcurrentHashMap
ConcurrentHashMap与HashTable比较
- 更好的并发性能,在你从中读取对象的时候 ConcurrentHashMap 并不会把整个 Map 锁住,只是把 Map 中正在被写入的部分进行锁定。
- 在被遍历的时候,即使是 ConcurrentHashMap 被改动,它也不会抛 ConcurrentModificationException。
ConcurrentNavigableMap
一个支持并发访问的 java.util.NavigableMap,它还能让它的子 map 具备并发访问的能力。
headMap
headMap(T toKey) 方法返回一个包含了小于给定 toKey 的 key 的子 map。
tailMap
tailMap(T fromKey) 方法返回一个包含了不小于给定 fromKey 的 key 的子 map。
subMap
subMap() 方法返回原始 map 中,键介于 from(包含) 和 to (不包含) 之间的子 map。
更多方法
- descendingKeySet()
- descendingMap()
- navigableKeySet()
CountDownLatch
CountDownLatch 是一个并发构造,它允许一个或多个线程等待一系列指定操作的完成。
- CountDownLatch 以一个给定的数量初始化。countDown() 每被调用一次,这一数量就减一。
- 通过调用 await() 方法之一,线程可以阻塞等待这一数量到达零。
CyclicBarrier
CyclicBarrier 类是一种同步机制,它能够对处理一些算法的线程实现同步。
更多实例参考: CyclicBarrier
Exchanger
Exchanger 类表示一种两个线程可以进行互相交换对象的会和点。
更多实例参考: Exchanger
Semaphore
Semaphore 类是一个计数信号量。具备两个主要方法:
- acquire()
- release()
- 每调用一次 acquire(),一个许可会被调用线程取走。
- 每调用一次 release(),一个许可会被返还给信号量。
Semaphore 用法
- 保护一个重要(代码)部分防止一次超过 N 个线程进入。
- 在两个线程之间发送信号。
保护重要部分
如果你将信号量用于保护一个重要部分,试图进入这一部分的代码通常会首先尝试获得一个许可,然后才能进入重要部分(代码块),执行完之后,再把许可释放掉。
Semaphore semaphore = new Semaphore(1);
//critical section
semaphore.acquire();
...
semaphore.release();
- 1
- 2
- 3
- 4
- 5
在线程之间发送信号
如果你将一个信号量用于在两个线程之间传送信号,通常你应该用一个线程调用 acquire() 方法,而另一个线程调用 release() 方法。
- 如果没有可用的许可,acquire() 调用将会阻塞,直到一个许可被另一个线程释放出来。
- 如果无法往信号量释放更多许可时,一个 release() 调用也会阻塞。
公平性
无法担保掉第一个调用 acquire() 的线程会是第一个获得一个许可的线程。
可以通过如下来强制公平:
Semaphore semaphore = new Semaphore(1, true);
- 1
- 需要注意,强制公平会影响到并发性能,建议不使用。
ExecutorService
这里之前有过简单的总结: Java 中几种常用的线程池
存在于 java.util.concurrent 包里的 ExecutorService 实现就是一个线程池实现。
实现类
此接口实现类包括:
- ScheduledThreadPoolExecutor : 通过
Executors.newScheduledThreadPool(10)
创建的 - ThreadPoolExecutor: 除了第一种的
其他三种方式
创建的
相关方法
- execute(Runnable):
无法得知被执行的 Runnable 的执行结果 - submit(Runnable):
返回一个 Future 对象,可以知道Runnable 是否执行完毕。 - submit(Callable):
Callable 实例除了它的 call() 方法能够返回一个结果,通过Future可以获取。 - invokeAny(…):
传入一系列的 Callable 或者其子接口的实例对象,无法保证返回的是哪个 Callable 的结果 ,只能表明其中一个已执行结束。
如果其中一个任务执行结束(或者抛了一个异常),其他 Callable 将被取消。 - invokeAll(…):
返回一系列的 Future 对象,通过它们你可以获取每个 Callable 的执行结果。
关闭ExecutorService
- shutdown() : 不会立即关闭,但它将不再接受新的任务
- shutdownNow(): 立即关闭
ThreadPoolExecutor
- ThreadPoolExecutor 使用其内部池中的线程执行给定任务(Callable 或者 Runnable)。
ScheduledExecutorService(接口,其实现类为ScheduledThreadPoolExecutor)
- ScheduledExecutorService能够将任务延后执行,或者间隔固定时间多次执行。
- ScheduledExecutorService中的 任务由一个工作者线程异步执行,而不是由提交任务给 ScheduledExecutorService 的那个线程执行。
相关方法
- schedule (Callable task, long delay, TimeUnit timeunit):
Callable 在给定的延迟之后执行,并返回结果。 - schedule (Runnable task, long delay, TimeUnit timeunit)
除了 Runnable 无法返回一个结果之外,和第一个方法类似。 - scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
这一方法规划一个任务将被定期执行。该任务将会在首个 initialDelay 之后得到执行,然后每个 period 时间之后重复执行。
period 被解释为前一个执行的开始和下一个执行的开始之间的间隔时间。 - scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)
和上一个方法类似,只是period 则被解释为前一个执行的结束和下一个执行的结束之间的间隔。
ForkJoinPool
ForkJoinPool 在 Java 7 中被引入。它和 ExecutorService 很相似,除了一点不同。ForkJoinPool 让我们可以很方便地把任务分裂成几个更小的任务,这些分裂出来的任务也将会提交给 ForkJoinPool。
用法参考:Java Fork and Join using ForkJoinPool
Lock
Lock 是一个类似于 synchronized 块的线程同步机制。但是 Lock 比 synchronized 块更加灵活、精细。
实现类
Lock是一个接口,其实现类包括:
- ReentrantLock
示例
Lock lock = new ReentrantLock();
lock.lock();
//critical section
lock.unlock();
- 1
- 2
- 3
- 4
- 调用
lock() 方法
之后,这个 lock 实例就被锁住啦。 - 当lock示例被锁后,任何其他再过来调用 lock() 方法的线程将会被阻塞住,直到调用了unlock() 方法。
- unlock() 被调用了,lock 对象解锁了,其他线程可以对它进行锁定了。
Lock 和 synchronized区别
- synchronized 代码块不能够保证进入访问等待的线程的先后顺序。
- 你不能够传递任何参数给一个 synchronized 代码块的入口。因此,对于 synchronized 代码块的访问等待设置超时时间是不可能的事情。
- synchronized 块必须被完整地包含在单个方法里。而一个 Lock 对象可以把它的 lock() 和 unlock() 方法的调用放在不同的方法里。
ReadWriteLock
读写锁一种先进的线程锁机制。
- 允许多个线程在同一时间对某特定资源进行读取,
- 但同一时间内只能有一个线程对其进行写入。
实现类
- ReentrantReadWriteLock
规则
- 如果没有任何写操作锁定,那么可以有多个读操作锁定该锁
- 如果没有任何读操作或者写操作,只能有一个写线程对该锁进行锁定。
示例:
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.readLock().lock();
// multiple readers can enter this section
// if not locked for writing, and not writers waiting
// to lock for writing.
readWriteLock.readLock().unlock();
readWriteLock.writeLock().lock();
// only one writer can enter this section,
// and only if no threads are currently reading.
readWriteLock.writeLock().unlock();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
更多原子性包装类
位于 atomic
包下,包含一系列原子性变量。
- AtomicBoolean
- AtomicInteger
- AtomicLong
- AtomicReference
…
参考资料: Java 并发工具包 java.util.concurrent 用户指南
java.util.concurrency - Java Concurrency Utilities
扩展阅读:
深入浅出 Java Concurrency: 讲解的很详细
Java 并发工具箱之concurrent包的更多相关文章
- Java并发编程:Concurrent锁机制解析
Java并发编程:Concurrent锁机制解析 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: # ...
- java util 下的concurrent包
------------------------------------------java util 下的concurrent包--------并发包--------------------.jav ...
- java多线程之Concurrent包
1.在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题. 2.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的 ...
- 并发知识与concurrent包
要想进入一线互联网公司,这部分内容必须要会,否则的话,你始终都只能停留在比较low的段位. 关于并发知识,最重要的两个概念一定要搞清楚,那就是可见性和原子性.其中可见性与前面提到的volatile关键 ...
- Java并发机制(8)--concurrent包下辅助类的使用
Java并发编程:concurrent包下辅助类的使用 整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920397.html 1.CountDown ...
- 【并发编程】【JDK源码】JDK的(J.U.C)java.util.concurrent包结构
本文从JDK源码包中截取出concurrent包的所有类,对该包整体结构进行一个概述. 在JDK1.5之前,Java中要进行并发编程时,通常需要由程序员独立完成代码实现.当然也有一些开源的框架提供了这 ...
- 并发工具箱 concurrent包的原理分析以及使用
1.java.util.concurrent 包下的类分类图 locks部分:显式锁(互斥锁和速写锁)相关: atomic部分:原子变量类相关,是构建非阻塞算法的基础: executor部分:线程池相 ...
- 高并发编程基础(java.util.concurrent包常见类基础)
JDK5中添加了新的java.util.concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法 ...
- 线程并发线程安全介绍及java.util.concurrent包下类介绍
线程Thread,在Java开发中多线程是必不可少的,但是真正能用好的并不多! 首先开启一个线程三种方式 ①new Thread(Runnable).start() ②thread.start(); ...
随机推荐
- Python学习之字典--三级菜单
效果图: 实现代码: dic = { '人物':{ '帽子':{'前年玄铁帽'}, '武器':{'屠龙宝刀'} }, '属性':{ '力量':{35}, '敏捷':{66} }, '任务':{ '主线 ...
- 关于开启Eureka安全Security认证后,客户端死活注册不上的问题
遇到一个问题"开启Eureka服务端的安全认证后,客户端死活注册不到Eureka上",已经尝试了以下办法,完全搞不定... 客户端出错的版本: spring-boot:2.0.3. ...
- Redis源码解析:27集群(三)主从复制、故障转移
一:主从复制 在集群中,为了保证集群的健壮性,通常设置一部分集群节点为主节点,另一部分集群节点为这些主节点的从节点.一般情况下,需要保证每个主节点至少有一个从节点. 集群初始化时,每个集群节点都是以独 ...
- linux下文件操作之cp和mv
Linux CP文件夹略过目录的解决 root@QGY:/home/qgy# cp image/newimage_raw /mnt/4T/qin/cp: 略过目录'image/newimage_raw ...
- PHP--时间格式处理
Ymd格式转Y-m-d或转成时间戳将Ymd格式如19930811转成1993-08-11格式 date('Y-m-d',strtotime('19930811') 将Ymd格式如19930811转成时 ...
- fore end common url
1.Fore end course 1)less http://www.bootcss.com/p/lesscss/2.Fore end official website 1)W3C(HTML/CSS ...
- [转]深入理解定位父级offsetParent及偏移大小
偏移量(offset dimension)是javascript中的一个重要的概念.涉及到偏移量的主要是offsetLeft.offsetTop.offsetHeight.offsetWidth这四个 ...
- 做网站-Http状态码详解
https://mp.weixin.qq.com/s/ZcYG59yLsLCNY2-2k4YqwA HTTP状态码(HTTP Status Code)是用以表示网页服务器HTTP响应状态的3位数字代码 ...
- Java review-basic2
1.Implement a thread-safe (blocking) queue: Class Producer implements Runable{ Private final Blockin ...
- angular4 动态创建组件 vs 动态创建模板
实现 模拟场景:页面上"帮助"按钮的点击触发帮助文档的弹出框,且每个页面的帮助文档不一样 因此弹出框里的帮助文档是一个动态模板而不是动态组件 以下comp均代表Type类型的动态 ...