Java 8并发工具包简介

转自:http://www.toutiao.com/a6399027925228077313/

Java 8并发工具包由3个包组成,分别是java.util.concurrent、java.util.concurrent.atomic和java.util.concurrent.locks,提供了大量关于并发的接口、类、原子操作类、锁相关类。借助java.util.concurrent包,可以非常轻松地实现复杂的并发操作。java.util.concurrent包主要包含以下内容,后文将具体介绍:

阻塞队列:多种阻塞队列的实现,在队列为空或满时能够阻塞消费者或生产者相关线程。

并发容器:用于并发场景的容器类型,一般无需加锁。

线程池:创建单线程、固定大小或可缓存的线程池,支持周期性任务,也能够实现异步任务的返回。

锁:用于并发同步的锁;

原子类型:用于实现原子操作的数据类型,包括AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference等

并发工具:用于并发场景的类,可以控制并发线程的同步、等待。

阻塞队列BlockingQueue

在BlockingQueue中,生产者可以持续向队列插入新的元素,直到队列满为止,队列满后生产者线程被阻塞;消费者可以持续从队列取出元素,直到队列空为止,队列空后消费者线程被阻塞。

BlockingQueue提供了四种类型的操作方法,在操作不能立即执行的情况下有不同的表现。

  抛出异常 返回特殊值 阻塞 超时返回
插入 add(o) offer(o) put(o) offer(o, timeout, timeunit)
删除 remove(o) poll take poll(timeout, timeunit)
检查 element peek    

Java 8提供了多种类型的BlockingQueue实现类,

ArrayBlockingQueue:基于数组实现的有界阻塞队列,创建后不能修改队列的大小;

LinkedBlockingQueue:基于链表实现的有界阻塞队列,默认大小为Integer.MAX_VALUE,有较好的吞吐量,但可预测性差。

PriorityBlockingQueue:具有优先级的无界阻塞队列,不允许插入null,所有元素都必须可比较(即实现Comparable接口)。

SynchronousQueue:只有一个元素的同步队列。若队列中有元素插入操作将被阻塞,直到队列中的元素被其他线程取走。

DelayQueue:无界阻塞队列,每个元素都有一个延迟时间,在延迟时间之后才释放元素。

阻塞双端队列BlockingDueue

Dueue是“Double Ended Queue”的缩写。生产者和消费者可以在队列的两端进行插入和删除操作。

在头部提供了四种类型的操作方法,在操作不能立即执行的情况下有不同的表现。

  抛出异常 返回特殊值 阻塞 超时返回
插入 addFirst(o) offerFirst(o) putFirst(o) offerFirst(o, timeout, timeunit)
删除 removeFirst(o) pollFirst(o) takeFirst(o) pollFirst(timeout, timeunit)
检查 getFirst(o) peekFirst(o)    

在尾部提供了四种类型的操作方法,在操作不能立即执行的情况下有不同的表现。

  抛出异常 返回特殊值 阻塞 超时返回
插入 addLast(o) offerLast(o) putLast(o) offerLast(o, timeout, timeunit)
删除 removeLast(o) pollLast(o) takeLast(o) pollLast(timeout, timeunit)
检查 getLast(o) peekLast(o)    

Java 8只提供了一种类型的BlockingDueue实现类,

LinkedBlockingDeque:基于双向链表实现的有界阻塞队列,默认大小为Integer.MAX_VALUE,有较好的吞吐量,但可预测性差。

阻塞转移队列TransferQueue

TransferQueue接口继承了BlockingQueue接口,因此具有BlockingQueue接口的所有方法,并增加了一些方法。方法及作用如下:

tryTransfer(E e):若当前存在一个正在等待获取的消费者线程,则该方法会即刻转移e,并返回true;若不存在则返回false,但是并不会将e插入到队列中。这个方法不会阻塞当前线程,要么快速返回true,要么快速返回false。

transfer(E e):若当前存在一个正在等待获取的消费者线程,即立刻将e移交之;否则将元素e插入到队列尾部,并且当前线程进入阻塞状态,直到有消费者线程取走该元素。

tryTransfer(E e, long timeout, TimeUnit unit):若当前存在一个正在等待获取的消费者线程,会立即传输给它; 否则将元素e插入到队列尾部,并且等待被消费者线程获取消费掉。若在指定的时间内元素e无法被消费者线程获取,则返回false,同时该元素从队列中移除。

Java 8 提供了一个基于链表的实现类LinkedTransferQueue。

并发容器

工具包提供了队列的并发实现类ConcurrentLinkedQueue和ConcurrentLinkedDeque,两者都是无界非阻塞线程安全的队列。

ConcurrentMap接口继承了普通的Map接口,提供了线程安全和原子操作特性。Java 8 提供了实现类ConcurrentHashMap,ConcurrentHashMap不锁定整个Map,只锁定需要写入的部分,因此并发性能比HashTable要高很多。

ConcurrentNavigableMap接口继承了ConcurrentMap和NavigableMap接口,支持并发访问NavigableMap,还能让子Map具备并发访问的能力。NavigableMap是扩展的 SortedMap,具有了针对给定搜索目标返回最接近匹配项的导航方法。

Java 8 提供了实现类ConcurrentSkipListMap,并没有使用lock来保证线程的并发访问和修改,而是使用了非阻塞算法来保证并发访问,高并发时相对于TreeMap有明显的优势。

工具包提供了NavigableSet的并发实现类ConcurrentSkipListSet,是线程安全的有序集合,适用于高并发的场景,通过ConcurrentSkipListMap实现。

工具包提供了两个写时复制容器,即CopyOnWriteArrayList和CopyOnWriteArraySet。写时复制技术是一种优化策略,多个线程可以并发访问同一份数据,当有线程要修改时才进行复制然后修改。在Linux系统中,fork进程后,子进程先与父进程共享数据,需要修改时才用写时复制得到自己的副本。在Java中,写时复制容器在修改数据后,把原来容器的引用指向新容器,来实现读写分离,在并发读写中不需要加锁。写时复制容器适用于读多写少的场景,在复制时会占用较多内存,能够保证最终一致性,但无法保证瞬时一致性。

线程池

工具包中Executor接口定义了执行器的基本功能,即execute方法,接收Runnable对象参数并执行Runnable中的操作。

ExecutorService接口继承Executor接口后增加了关于执行器服务的定义,如关闭、立即关闭、检查关闭、等待终止、提交有返回值的任务、批量提交任务等。通过Executors的工厂方法获取ExecutorService的具体实现,目前Executors可以返回的实现类型如下:

FixedThreadPool:固定大小的线程池,创建时指定大小;

WorkStealingPool:拥有多个任务队列(以便减少连接数)的线程池;

SingleThreadExecutor:单线程执行器,顾名思义只有一个线程执行任务;

CachedThreadPool:根据需要创建线程,可以重复利用已存在的线程来执行任务;

SingleThreadScheduledExecutor:根据时间计划延迟创建单个工作线程或者周期性创建的单线程执行器;

ScheduledThreadPool:能够延后执行任务,或者按照固定的周期执行任务。

如果希望在任务执行完成后得到任务的返回值,可以调用submit方法传入Callable任务,并通过返回的Future对象查看任务执行是否完成,并获取返回值。

线程分叉与合并

ForkJoinPool 让我们可以很方便地把任务分裂成几个更小的任务,这些分裂出来的任务也将会提交给 ForkJoinPool。任务可以继续分割成更小的子任务,只要它还能分割。分叉和合并原理包含两个递归进行的步骤。两个步骤分别是分叉步骤和合并步骤。

一个使用了分叉和合并原理的任务可以将自己分叉(分割)为更小的子任务,这些子任务可以被并发执行。如下图所示:

通过把自己分割成多个子任务,每个子任务可以由不同的 CPU 并行执行,或者被同一个 CPU 上的不同线程执行。

只有当给的任务过大,把它分割成几个子任务才有意义。把任务分割成子任务有一定开销,因此对于小型任务,这个分割的消耗可能比每个子任务并发执行的消耗还要大。

什么时候把一个任务分割成子任务是有意义的,这个界限也称作一个阀值。这要看每个任务对有意义阀值的决定。很大程度上取决于它要做的工作的种类。

当一个任务将自己分割成若干子任务之后,该任务将等待所有子任务结束。一旦子任务执行结束,该任务可以把所有结果合并到同一个结果。图示如下:

使用锁实现的同步机制很像synchronized块,但是比synchronized块更灵活。锁和synchronized的主要区别在于:

Synchronized块不能保证等待进入块的线程的访问顺序;

Synchronized块无法接收参数,不能在有超时时间限制的情况下尝试访问;

Synchronized块必须包含在单个方法中,而锁的lock和unlock操作可以在单独的方法中。

工具包提供了以下几种类型的锁:

ReadWriteLock:读写锁接口,允许多个线程读取某个资源,但是一次只能有一个线程进行写操作。内部有读锁、写锁两个接口,分别保护读操作和写操作。实现类为ReentrantReadWriteLock。

ReentrantLock:可重入锁,具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定,此方法将立即返回。内部有一个计数器,拥有锁的线程每锁定一次,计数器加1,每释放一次计数器减1。

原子类型

工具包提供了一些可以用原子方式进行读写的变量类型,支持无锁线程安全的单变量编程。

本质上,这些类都扩展了volatile的概念,使用一个volatile类型的变量来存储实际数据。

工具包提供了4种类型的原子变量类型:

AtomicBoolean:可原子操作的布尔对象;

AtomicInteger:可原子操作的整形对象;

AtomicLong:可原子操作的长整形对象;

AtomicReference:可原子操作的对象引用。

以AtomicInteger为例。在Java中i++和++i操作并不是线程安全的,需要加锁。AtomicInteger提供了以下几种线程安全的操作方法:

方法 定义 作用
getAndIncrement public final int getAndIncrement i++
getAndDecrement public final int getAndDecrement i--
incrementAndGet public final int incrementAndGet ++i
decrementAndGet public final int decrementAndGet --i
getAndAdd public final int getAndAdd(int delta) 增加delta返回旧值
addAndGet public final int addAndGet(int delta) 增加delta返回新值

在此基础上,工具包还提供了原子性的数组类型,包括AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。

并发工具

CountDownLatch

CountDownLatch用于一个或者多个线程等待一系列指定操作的完成。初始化时,给定一个数量,每调用一次countDown 方法数量减一。其他线程调用await方法等待时,线程会阻塞到数量减到0才开始执行。

CyclicBarrier 栅栏

CyclicBarrier是一种同步机制,它能够对处理一些算法的线程实现同步。换句话讲,它就是一个所有线程必须等待的一个栅栏,直到所有线程都到达这里,然后所有线程才可以继续做其他事情。在下图的流程中,线程1和线程2都到达第一个栅栏后才能够继续运行。如果线程1先到线程2后到,则线程1需要等待线程2到达栅栏处,然后两个线程才能继续运行。

Exchanger 交换机

Exchanger类表示一种会合点,两个线程可以在这里交换对象。两个线程各自调用exchange方法进行交换,当线程A调用Exchange对象的exchange方法后,它会陷入阻塞状态,直到线程B也调用了exchange方法,然后以线程安全的方式交换数据,之后线程A和B继续运行。

Semaphore 信号量

Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire 获取一个许可,如果没有就等待,而 release 释放一个许可。

ThreadLocalRandom产生并发随机数

使用Math.random产生随机数,使用原子变量来保存当前的种子,这样两个线程同时调用序列时得到的是伪随机数,而不是相同数量的两倍。ThreadLocalRandom提供并发产生的随机数,能够解决多个线程发生的竞争争夺。

时间: 2024-12-29 07:39:48

Java 8并发工具包简介的相关文章

Java 并发工具包 java.util.concurrent 大全

1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Java 的并发编程变得更加简单轻松的类.在这个包被添加以前,你需要自己去动手实现自己的相关工具类. 本文我将带你一一认识 java.util.concurrent 包里的这些类,然后你可以尝试着如何在项目中使用它们.本文中我将使用 Java 6 版本,我不确定这和 Java 5 版本里的是否有一些差异

Java_并发工具包 java.util.concurrent 用户指南(转)

译序 本指南根据 Jakob Jenkov 最新博客翻译,请随时关注博客更新:http://tutorials.jenkov.com/java-util-concurrent/index.html.本指南已做成中英文对照阅读版的 pdf 文档,有兴趣的朋友可以去 Java并发工具包java.util.concurrent用户指南中英文对照阅读版.pdf[带书签] 进行下载. 1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台

Lucene:基于Java的全文检索引擎简介 (zhuan)

http://www.chedong.com/tech/lucene.html ********************************************** Lucene是一个基于Java的全文索引工具包. 基于Java的全文索引引擎Lucene简介:关于作者和Lucene的历史 全文检索的实现:Luene全文索引和数据库索引的比较 中文切分词机制简介:基于词库和自动切分词算法的比较 具体的安装和使用简介:系统结构介绍和演示 Hacking Lucene:简化的查询分析器,删除的

Lucene:基于Java的全文检索引擎简介

Lucene是一个基于Java的全文索引工具包. 基于Java的全文索引引擎Lucene简介:关于作者和Lucene的历史 全文检索的实现:Luene全文索引和数据库索引的比较 中文切分词机制简介:基于词库和自动切分词算法的比较 具体的安装和使用简介:系统结构介绍和演示 Hacking Lucene:简化的查询分析器,删除的实现,定制的排序,应用接口的扩展 从Lucene我们还可以学到什么 另外,如果是在选择全文引擎,现在也许是试试 Sphinx的时候了:相比Lucene速度更快, 有中文分词的

使用JUC并发工具包的Lock和Condition,实现生产者和消费者问题中的有界缓存

JDK5.0之前,用java实现生产者和消费者的唯一方式就是使用synchronized内置锁和wait/notify条件通知机制.JDK5.0之后提供了显示锁Lock和条件队列Condition,与内置锁和内置条件队列相对应,但是显示的锁和条件队列,功能更强大,更灵活.此外JDK5.0之后还提供了大量很有用的并发工具类,如BlockingQueue等,基于这些数据结构,能够方便.快速.高效的构建自己应用需要的效果.这里我们简单使用下显示锁和条件队列,来模拟有界缓存的实现,功能类似于JDK内置的

JDK并发工具包CompletionService和ExecutorCompletionService的好处和使用场景

<Java并发编程实践>一书6.3.5节CompletionService:Executor和BlockingQueue,有这样一段话: "如果向Executor提交了一组计算任务,并且希望在计算完成后获得结果,那么可以保留与每个任务关联的Future,然后反复使用get方法,同时将参数timeout指定为0,从而通过轮询来判断任务是否完成.这种方法虽然可行,但却有些繁琐.幸运的是,还有一种更好的方法:完成服务CompletionService." 这是什么意思呢?我们通过

Java高并发是不是你的菜??

自从JAVA5.0增加了最初由DougLea编写的高质量的.广泛使用的.并发实用程序util.concurrent并变成了JSR-166的新包之后,在Java内置所提供的类库中,就提供了越来越多的并发编程的实用工具类.学习并掌握这些技术对于专注于Java并发编程的开发人员来讲是基本的公里,随着Java版本的不断更新与改进,开发人员可以通过Java新版本所带来的新特性,无需从头重新编写并发程序工具类. 我们该学习Java并发嘛? 我们该如何学习Java并发? CPU这么多核了,我们如何更好的利用?

Java高并发程序设计

一.并行世界 摩尔定律的失效,2004年秋季,Intel宣布彻底取消4GHz计划,CPU向多核心的发展,顶级计算机科学家唐纳德丶尔文丶克努斯评价:在我看来,这种现象(并发)或多或少是由于硬件设计者已经无计可施导致的,他们将摩尔定律失效的责任推脱给软件开发者. 1.几个概念 同步和异步: 并发与并行:都表示多个任务同时执行,但并发偏重于多个任务交替执行. 临界区:即共享的数据 阻塞和非阻塞: 死锁.饥饿和活锁:死锁为互不让行,饥饿为一直拿不到资源,活锁为一直撞到 2.并发级别   由于临界区的存在

Java:并发不易,先学会用

我从事Java编程已经11年了,绝对是个老兵:但对于Java并发编程,我只能算是个新兵蛋子.我说这话估计要遭到某些高手的冷嘲热讽,但我并不感到害怕. 因为我知道,每年都会有很多很多的新人要加入Java编程的大军,他们对"并发"编程中遇到的问题也会有感到无助的时候.而我,非常乐意与他们一道,对使用Java线程进行并发程序开发的基础知识进行新一轮的学习. 本篇我们来谈谈 Java 并发编程:并发编程并不容易掌握,但很有必要掌握. 01.我们为什么要学习并发? 我的脑袋没有被如来佛祖开过光,