java.util.concurrent是并发包的目录。
阻塞队列 BlockingQueue接口通常用于一个线程生产对象,而另外一个线程消费这些对象的场景。该接口的具体实现有ArrayBlockingQueue,DelayQueue,LinkedBlockingQueue,PriorityBlockingQueue,SynchronousQueue。
DelayQueue 对元素进行持有直到一个特定的延迟到期。注入其中的元素必须实现java.util.concurrent.Delayed 接口,DelayQueue 将会在每个元素的 getDelay() 方法返回的值的时间段之后才释放掉该元素。
ArrayBlockingQueue 内部以 FIFO(先进先出)的顺序对元素进行存储。
LinkedBlockingQueue 内部以 FIFO(先进先出)的顺序对元素进行存储。
所有插入到具有优先级的阻塞队列PriorityBlockingQueue的元素必须实现 java.lang.Comparable 接口。因此该队列中元素的排序就取决于你自己的 Comparable 实现。
SynchronousQueue 是一个特殊的队列,它的内部同时只能够容纳单个元素。它更多像是一个汇合点。
阻塞双端队列BlockingDeque 继承自 BlockingQueue。如果生产者线程需要在队列的两端都可以插入数据,消费者线程需要在队列的两端都可以移除数据,这个时候也可以使用BlockingDeque。
链阻塞双端队列LinkedBlockingDeque 类实现了 BlockingDeque 接口。
ConcurrentMap 接口的实现类ConcurrentHashMap和 java.util.HashTable 类很相似,但 ConcurrentHashMap 能够提供比HashTable 更好的并发性能。在你从中读取对象的时候 ConcurrentHashMap 并不会把整个
Map 锁住。此外,在你向其中写入对象的时候,ConcurrentHashMap 也不会锁住整个 Map。它的内部只是把 Map 中正在被写入的部分进行锁定。
并发导航映射ConcurrentNavigableMap是一个支持并发访问的 java.util.NavigableMap,
headMap(T toKey) 方法返回一个包含了小于给定 toKey 的 key 的子 map。
tailMap(T fromKey) 方法返回一个包含了不小于给定 fromKey 的 key 的子 map。
subMap() 方法返回原始 map 中,键介于 from(包含) 和 to (不包含) 之间的子 map。
闭锁 CountDownLatch是一个并发构造,它允许一个或多个线程等待一系列指定操作的完成。CountDownLatch 以一个给定的数量初始化。countDown() 每被调用一次,这一数量就减一。通过调用 await() 方法之一,线程可以阻塞等待这一数量到达零。
栅栏 CyclicBarrier是一个所有线程必须等待的一个栅栏,直到所有线程都到达这里,然后所有线程才可以继续做其他事情。通过调用 CyclicBarrier 对象的 await()方法,两个线程可以实现互相等待。一旦 N 个线程在等待 CyclicBarrier 达成,
所有线程将被释放掉去继续运行。你也可以为等待线程设定一个超时时间。等待超过了超时时间之后,即便还没有达成N 个线程等待 CyclicBarrier 的条件,该线程也会被释放出来。
交换机 Exchanger类表示一种两个线程可以进行互相交换对象的会和点。.exchanger.exchange(xxx);
Semaphore 类是一个计数信号量。这就意味着它具备两个主要方法:acquire(),release()
计数信号量由一个指定数量的 "许可" 初始化。每调用一次 acquire(),一个许可会被调用线程取走。每调用一次 release(),一个许可会被返还给信号量。
信号量主要有两种用途:
1. 保护一个重要(代码)部分防止一次超过 N 个线程进入。
2. 在两个线程之间发送信号。
没有办法保证线程能够公平地可从信号量中获得许可。也就是说,无法担保掉第一个调用
acquire() 的线程会是第一个获得一个许可的线程。如果第一个线程在等待一个许可时发生
阻塞,而第二个线程前来索要一个许可的时候刚好有一个许可被释放出来,那么它就可能会
在第一个线程之前获得许可。
如果你想要强制公平,Semaphore 类有一个具有一个布尔类型的参数的构造子,通过这个参
数以告知 Semaphore 是否要强制公平。强制公平会影响到并发性能,所以除非你确实需要
它否则不要启用它。
执行器服务接口ExecutorService就是一个线程池实现。
既然 ExecutorService 是个接口,如果你想用它的话就得去使用它的实现类之一。
java.util.concurrent 包提供了 ExecutorService 接口的以下实现类:ThreadPoolExecutor,ScheduledThreadPoolExecutor。
但是你也可以使用 Executors 工厂类来
创建 ExecutorService 实例。以下是几个创建 ExecutorService 实例的例子:
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
ExecutorService executorService3 = Executors.newScheduledThreadPool(10);
有几种不同的方式来将任务委托给 ExecutorService 去执行:
execute(Runnable)
submit(Runnable)
submit(Callable)
invokeAny(...)
invokeAll(...)
要终止 ExecutorService 里的线程你需要调用 ExecutorService 的 shutdown() 方法。
ExecutorService 并不会立即关闭,但它将不再接受新的任务,而且一旦所有线程都完成了
当前任务的时候,ExecutorService 将会关闭。
ThreadPoolExecutor 是 ExecutorService 接 口 的 一 个 实 现 。
ThreadPoolExecutor 包含的线程池能够包含不同数量的线程。池中线程的数量由以下变量决定:corePoolSize,maximumPoolSize
当一个任务委托给线程池时,如果池中线程数量低于 corePoolSize,一个新的线程将被创建,即使池中可能尚有空闲线程。
如果内部任务队列已满,而且有至少 corePoolSize 正在运行,但是运行线程的数量低于maximumPoolSize,一个新的线程将被创建去执行该任务。
但是,除非你确实需要显式为 ThreadPoolExecutor 定 义 所 有 参 数 , 使 用java.util.concurrent.Executors 类中的工厂方法之一会更加方便,正如 ExecutorService 小节所述。
定时执行者服务.ScheduledExecutorService 是一个 ExecutorService,它能够将任务延后执行,或者间隔固定时间多次执行。具有以下实现类:ScheduledThreadPoolExecutor
也可以使用Executors 工厂类来创建一个 ScheduledExecutorService 实例。比如:ScheduledExecutorService scheduledExecutorService =Executors.newScheduledThreadPool(5);
一旦你创建了一个 ScheduledExecutorService,你可以通过调用它的以下方法:
schedule (Callable task, long delay, TimeUnit timeunit)
schedule (Runnable task, long delay, TimeUnit timeunit)
scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)
ForkJoinPool 让我们可以很方便地把任务分裂成几个更小的任务,这些分裂出来的任务也将会提交给 ForkJoinPool。任务可以继续分割成更小的子任务,只要它还能分割。
通过把自己分割成多个子任务,每个子任务可以由不同的 CPU 并行执行,或者被同一个CPU 上的不同线程执行。只有当给的任务过大,把它分割成几个子任务才有意义。把任务分割成子任务有一定开销,
因此对于小型任务,这个分割的消耗可能比每个子任务并发执行的消耗还要大。
什么时候把一个任务分割成子任务是有意义的,这个界限也称作一个阀值。这要看每个任务对有意义阀值的决定。很大程度上取决于它要做的工作的种类。
当一个任务将自己分割成若干子任务之后,该任务将进入等待所有子任务的结束之中。一旦子任务执行结束,该任务可以把所有结果合并到同一个结果。
就像提交任务到 ExecutorService 那样,把任务提交到 ForkJoinPool。你可以提交两种类型的任务。一种是没有任何返回值的(一个 "行动"),另一种是有返回值的(一个"任务")。这两种类型分别由 RecursiveAction 和 RecursiveTask 表示。
锁 Lock是一个类似于 synchronized 块的线程同步机制。但是 Lock 比 synchronized 块更加灵活、精细。
Lock 和 synchronized 代码块的主要不同点
synchronized 代码块不能够保证进入访问等待的线程的先后顺序。你不能够传递任何参数给一个 synchronized 代码块的入口。因此,对于 synchronized
代码块的访问等待设置超时时间是不可能的事情。synchronized 块必须被完整地包含在单个方法里。而一个 Lock 对象可以把它的 lock() 和 unlock() 方法的调用放在不同的方法里。
读写锁 ReadWriteLock允许多个线程在同一时间对某特定资源进行读取,但同一时间内只能有一个线程对其进行写入。
ReadWriteLock 接口有以下实现类:ReentrantReadWriteLock
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.readLock().lock();
readWriteLock.readLock().unlock();
readWriteLock.writeLock().lock();
readWriteLock.writeLock().unlock();
原子性布尔 AtomicBoolean类为我们提供了一个可以用原子方式进行读和写的布尔值,它还拥有一些先进的原子性操作,比如 compareAndSet()。
原子性整型 AtomicInteger
原子性长整型 AtomicLong
原子性引用型 AtomicReference