概述

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: 同步队列

使用示例:

见: BlockingQueue

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包的更多相关文章

  1. Java并发编程:Concurrent锁机制解析

    Java并发编程:Concurrent锁机制解析 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: # ...

  2. java util 下的concurrent包

    ------------------------------------------java util 下的concurrent包--------并发包--------------------.jav ...

  3. java多线程之Concurrent包

    1.在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题. 2.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的 ...

  4. 并发知识与concurrent包

    要想进入一线互联网公司,这部分内容必须要会,否则的话,你始终都只能停留在比较low的段位. 关于并发知识,最重要的两个概念一定要搞清楚,那就是可见性和原子性.其中可见性与前面提到的volatile关键 ...

  5. Java并发机制(8)--concurrent包下辅助类的使用

    Java并发编程:concurrent包下辅助类的使用 整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920397.html 1.CountDown ...

  6. 【并发编程】【JDK源码】JDK的(J.U.C)java.util.concurrent包结构

    本文从JDK源码包中截取出concurrent包的所有类,对该包整体结构进行一个概述. 在JDK1.5之前,Java中要进行并发编程时,通常需要由程序员独立完成代码实现.当然也有一些开源的框架提供了这 ...

  7. 并发工具箱 concurrent包的原理分析以及使用

    1.java.util.concurrent 包下的类分类图 locks部分:显式锁(互斥锁和速写锁)相关: atomic部分:原子变量类相关,是构建非阻塞算法的基础: executor部分:线程池相 ...

  8. 高并发编程基础(java.util.concurrent包常见类基础)

    JDK5中添加了新的java.util.concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法 ...

  9. 线程并发线程安全介绍及java.util.concurrent包下类介绍

    线程Thread,在Java开发中多线程是必不可少的,但是真正能用好的并不多! 首先开启一个线程三种方式 ①new Thread(Runnable).start() ②thread.start(); ...

随机推荐

  1. Python爬虫笔记【一】模拟用户访问之验证码清理(4)

    清理图片,对图片进行二值化,去边框,去干扰线,去点 from PIL import Image from pytesseract import * from fnmatch import fnmatc ...

  2. 安装配置flask环境

    安装 Flask 好的,让我们开始吧! 现在我们必须开始安装 Flask 以及一些我们会用到的扩展.我首选的方式就是创建一个虚拟环境,这个环境能够安装所有的东西,而你的主 Python 不会受到影响. ...

  3. /etc/inittab配置文件详解

    init的进程号是1,从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序,init程序需要读取设置文件/etc/inittab.inittab是个 ...

  4. android 数据绑定(3)自动更新UI

    1.官方文档 https://developer.android.com/topic/libraries/data-binding/observability 2.observable 属性 适合对象 ...

  5. Python中的一些模块用法

    python中os模块用法 python之模块之shutil模块 -------------------------------os模块-------------------------------- ...

  6. JQuery或JS判断浏览器内核版本号以及是否支持W3C盒子模型

    jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support .在更新的 2.0 版本中,将不再支持 IE 6/7/8. ...

  7. 项目中的那些事---下载pdf文件

    最近做了一个下载pdf文档的需求,本以为使用HTML5中<a>标签的属性download就能简单搞定,不料IE竟然不支持这一简单粗暴的H5新特性,而是直接在网页中打开, 于是各种搜索之后得 ...

  8. 洛谷P2859 [USACO06FEB]摊位预订Stall Reservations

    P2859 [USACO06FEB]摊位预订Stall Reservations 题目描述 Oh those picky N (1 <= N <= 50,000) cows! They a ...

  9. Leetcode617.Merge Two Binary Trees合并二叉树

    给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠. 你需要将他们合并为一个新的二叉树.合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 ...

  10. C++学习笔记----3.2 C++引用在本质上是什么,它和指针到底有什么区别

    从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有 ...