暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java并发包

释福 2021-08-30
195


  序言

    Java.util.concurrent包下日常用接口与类作个小说明,资料参考来自

    http://tutorials.jenkov.com/java-util-concurrent/index.html


1、阻塞队列(接口):BlockingQueue

 主要方法:


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

操作无法立即执行:

    抛异常:抛出一个异常。

    特定值:返回一个特定值(常为true/false)。

    阻塞:发生阻塞,直至可执行。

    超时:发生阻塞,直至可执行或超时。返回一个特定值告知是否执行成功(典型的是true/false)。


注意:BlockingQueue无法插入null,试图插入抛NullPointException


实现: 

    ArrayBlocking、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue。


2、数组阻塞队列:ArrayBlockingQueue

    有界的阻塞队列,内部实现是将对象放在一个数组里,以FIFO(先进先出)的顺序对元素进行存储。队列头元素放入时间最久,队列尾元素放入时间最短。


3、延迟队列:DelayQueue

    注入其中的元素需实现java.until.concurrent.Delayed接口,元素被持有到一个特定的延迟到期。

    java.until.concurrent.Delayed 接口定义:

public interface Delayed extends Comparable<Delayed> {

    public long getDelay(TimeUnit timeunit);

}

    DelayQueue将会在每个元素在getDelayed()方法返回值的时间段后才释放该元素。若返回0或负值,将被认为过期,在DelayQueue的下一次take调用时被释放。


4、链阻塞队列:LinkedBlockingQueue

    内部以一个链式结构(链接节点)对其元素进行存储,以FIFO(先进先出)的顺序进行存储。队列头元素放入时间最长,队列尾元素放入时间最短。

    元素个数上限默认为:Integer.MAX_VALUE。


5、有优先级的队列:PriorityBlockingQueue

    无界的并发队列,插入元素需实现java.lang.Comparable接口(排序所需)。其使用了java.until.PriorityQueue一样的排序规则。

    注:从PriorityBlockingQueue获取一个Integer,该Integer不保证其对元素的遍历以优先级为序。


6、同步队列:SynchronousQueue

    内部同时只能容纳一个元素。

    若队列中已有一个元素,再试图向其插入新元素的线程将会被阻塞,直至原旧元素被取走方可进行插入。


7、阻塞双端队列(接口,继承了BlockingQueue):BlockingDeque

    可以从任意一端插入或者抽取元素的队列。

    在不能插入元素时,它将阻塞试图插入元素的线程;在不能抽取元素时,它将阻塞住试图抽取元素的线程。

   

方法:

队列头部:


抛异常特定值
阻塞
超时
插入
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)offetLast(o, timeout, timeunit)
移除
removeLast(o)pollLast(o)
takeLast(o)pollLast(timeout, timeunit)
检查getLast(o)peekLast(o)


操作无法立即执行:

    抛异常:抛出一个异常。

    特定值:返回一个特定值(常为true/false)。

    阻塞:发生阻塞,直至可执行。

    超时:发生阻塞,直至可执行或超时。返回一个特定值告知是否执行成功(典型的是true/false)。


    实现:

    LinkedBlockingDeque


8、链阻塞双端队列:LinkedBlockingDeque

    双端队列,在它为空的时间,一个试图从中抽取的线程将会被阻塞,无论是从那端抽取数据。


9、并发映射Map(映射,接口):ConcurrentMap

    一个能够对别人的访问(插入与提取)进行并发处理的java.until.Map。

    从其父接口java.until.Map继承来的方法外还有额外的原子性方法。


    实现:

    ConcurrentHashMap

    和 java.until.HashTable 类很相似,但ConcurrentHashMap 能够提供比 HashTable 更好的并发性能。

    与java.until.HashTable 的不同点:

    在你从中读取、写入对象的时候ConcurrentHashMap并不会把整个Map锁住,其内部只把Map中正在被写入的部分进行锁定。

    在被遍历时,即便ConcurrentHashMap被改动,它也不会抛ConcurrentModificationException。尽管Iterator的设计不是为多个线程使用。


10、并发导航映射:ConcurrentNavigableMap

    一个支持并发访问的java.until.NavigableMap,它还能让它的子Map具备并发访问的能力。

  

    “子 Map”:headMap(),subMap(),tailMap() 之类的方法返回的Map。

    

    对原始 map 里的元素做了改动,这些改动将影响到子map中的元素(map 集合持有的其实只是对像的引用)。


    headMap(T key) :返回一个包含了小于给定 key 的子 map。

    tailMap(T key):返回一个包含了不小于给定 key 的子 map。

    subMap() : 返回原始 map 中,键介于 from (包含)和 to (不包含)之间的子 map。


11、  闭锁:CountDownLatch

    这是一个并发构造,它允许一个或多个线程等待一系列指定操作的完成。其以一个给定的数量初始化,每被调用一次,这一数量就减一。通过调用await() 方法之一,线程可阻塞等待这一数量级达到零。


12、栅栏:CyclicBarrier

    这是一种同步机制,能够对处理一些算法的线程实现同步。

    换言之,这就是一个所有线程必须等待的一个栅栏,直到所有线程都到达这里,然后所有线程才可以继续做其他事情。

    

    满足以下任何条件均可以让等待 CyclicBarrier 线程释放:

    最后一个线程也到达 CyclicBarrier (调用 await());

    当前线程被其他线程打断(其他线程调用了这个线程的interrupt() 方法);

    其他等待栅栏的线程被打断;

    其他等待栅栏的线程因超时而被释放;

    外部线程调用了栅栏的 CyclicBarrier.reset() 方法。


13、交换机:Exchanger

    这是一种两个线程可进行互相交换对象的回合点。


14、信号量:Semaphore

    这是一个计数信号量。这意味着它具备两个主要方法:

    acquire()

    release()

    计数信号量由一个指定数量的“许可”初始化。

    每调用一次 acquire(),一个许可会被调用线程取走。

    每调用一次release(), 一个许可会被返回给信号量。

    因此,在没有任何 release() 调用时,最多有 N 个线程能够调用 acquire()方法,N 是该信号量初始化时的许可的指定数量。

    这些许可只是一个简单的计数器。


    Semaphore 信号量的用法主要有两种:

        保护一个重要(代码)部分,防止一次超过 N 个线程进入;

        在两个线程之间发送信号。


15、执行器服务:ExecutorService

    这接口表示一个异步执行机制,使我们能够在后台执行任务。

   类似于一个线程池。存在于java.until.concurrent 包里的 ExecutorService 实现就是一个线程池的实现。

    java.until.concurrent 包里的重要实现:

    ThreadPoolExcutor;

    ScheduledThreadPoolExecutor。


方法 :

    execute(Runnable) :要求一个 java.lang.Runnable 对象,然后对它进行异步执行;

    submit(Runnable):要求一个 java.lang.Runnable 对象,返回一个 Future 对象;

    submit(Callable):类似于submit(Runnable) 方法,除了它所要求的参数类型之外。Callable 实例除了它的call() 方法能够返回一个结果之外和 一个Runnable很相像。Runnable.run() 不能返回一个结果。Callable 的结果可以通过 submit(Callable) 方法返回的 Future 对象进行获取。

    invokeAny():要求一系列的Callable或者子接口的示例对象。调用这个方法不会返回一个Future,但它返回其中一个已Callable对象的结果。无法保证返回的是哪个Callable结果,只能表明其中一个已执行结束;如果其中一个任务执行结束(或者抛了一个异常),其他Callable将被取消。

    invokeAll():调用你在集合中传给ExecutorService的所有Callable对象。返回一系列的 Future对象,通过它们可以获取每个Callable的执行结果。一个任务可能由于一个异常而结束,因此它可能没有“成功”。无法通过一个Future对象来告知我们是两种结束中的哪一种。


    ExecutorService 关闭:

    启动其ExecutorService的方法已结束,若应用中有一个活动的ExecutorService,它还将保持运行。ExecutorService里的活动线程阻止了JVM的关闭。需要调用ExecutorService的shutdown()方法,但并不会立即关闭,但它将不再接受新的任务,而且一旦所有线程都完成了当前任务的时候,ExecutorService将会关闭。在shutdown()被调用之前所有提交ExecutorService的任务都执行。

    立即关闭 ExecutorService,你可以调用shutdownNow() 方法。这个会立即尝试停止所有执行中的任务,并忽略那些已提交但尚未开始处理的任务。无法保证执行中任务的正确执行,可能他们被停止了,也可能已经执行结束。


16、线程执行者:ThreadPoolExecutor

    ExecutorService接口的一个是实现。

    其使用其内部线程池中的线程执行给定任务(Callable或Runnable)。

  其包含的线程池能够包含不同数量的线程,其线程池的数量由corePoolSize和maximumPoolSize决定。

    但一个任务委托给线程池时,若池中线程池数量低于corePoolSize,一个新的线程池将被创建,即使池中可能尚有空闲线程。

    若内部任务队列已满,而且至少有corePoolSize的线程任务在运行,但运行线程数低于maximumPoolSize,一个新的线程将被创建去执行任务。

    创建一个TheadPoolExecutor:

    new TheadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SENCONDS, new LinkedBlockingQueue<Runnable>());

    不需要显示定义所有参数,可用 java.until.concurrent.Executors 类中的工厂方法之一会更方便。


17、定时执行者服务:

    ScheduledExecutorService

    一个 ExecutorService,它能够将任务延后执行,或者间隔固定时间多次执行。

    任务由一个工作者线程异步执行,而不是由提交任务给ScheduledExecutorService的那个线程执行。


实现:

    ScheduledThreadPoolExecutor


方法:

    scheduled(Callable task, long delay, TimeUnit timeunit):指定的Callable在给定的延迟之后执行。方法返回一个ScheduleFuture,通过它可以在Callable任务执行之前对其进行取消,或在其执行之后获取结果。

    scheduled(Runnable task, long delay, TimeUnit timeunit):除了Runnable无法返回结果外,此方法就像Callable作为一个参数时的方法一样,因此 ScheduledFuture.get() 在任务执行结束之后返回 null。

    scheduledAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit timeunit):规划一个任务将被定期执行。该任务将会在首个 initialDelay 之后得到执行,然后每个 period 时间之后重复执行。如果给定任务的执行抛出了异常,该任务将不再执行。如果没有任务异常的话,这个任务将会持续循环执行到ScheduledExecutorService 关闭。如果一个任务占用了比计划的时间间隔更长的时候,下一次执行将在当前执行结束才开始。计划任务在同一时间不会由多个线程同时执行。

    scheduledWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit timeunit):除了period 有不同解释外,此方法和scheduledAtFixedRate很像。scheduledAtFixedRate() 方法中,period 被解释为前一个执行的开始和下一个执行开始之间的间隔时间。而在本方法中,period 则被解释为前一个执行的结束和下一个执行的结束之间的间隔。


    ScheduledExecutorService关闭:

    正如ExecutorService,在你使用结束之后,你需要把ScheduledExecutorService关闭掉。否则它将导致JVM继续运行,即使所有线程已经关闭。

    你可以用从ExecutorService接口继承来的shutdown() 或 shutdownNow() 方法将 ScheduledExecutorService 关闭。参照ExecutorService 关闭部分。


18、任务分叉与合并:ForkJoinPool

    Java7 时被引入。与 ExecutorService相似除了一点不同:ForkJoinPool让我们很方便比把任务分裂成几个更小的任务,这个分裂出来的任务也将会提交给 ForkJoinPool。任务可以继续分割成更小的子任务,只要它还能分割。


    分叉与合并

    分叉与合并原理包含两个递归进行的步骤。两个步骤分别时分叉步骤和合并步骤。


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


    合并

    当一个任务将自己分割成若干个子任务之后,该任务将进入等待所有子任务的结束直至。一旦子任务执行结束,该任务可以把所有结果合并到同一个结果。

    并非所有类型的任务都会返回一个结果,如果这个任务并不返回一个结果,它只需等待所有子任务执行完毕。


    创建一个 ForkJoinPool:

    new ForkJoinPool(4)。


    提交给 ForkJoinPool 的两种类型任务:

    没有返回值的“行动”:继承 RecursiveAction 的行动类型。

    有返回值的“任务”:继承 RecursiveTask 的任务类型。


19、锁(接口):Lock

    类似 synchronized 块的线程同步机制,比 synchronized 块更加灵活、精细。


    实现:

    ReentrantLock

    

    Lock 和 synchronized 代码块的主要不同点:

    一个 Lock 对象和一个 synchronized 代码块之间的主要不同点:

    synchronized 代码块不能保证进入访问等待的线程的先后顺序。

    不能传递任何参数给一个 synchronized 代码块的入口。因此,无法对与 synchronized 代码块的访问设置超时时间。

    synchronized 快必须被完整地包含在单个方法里。而一个 Lock 对象可以把它的 lock() 和 unlock() 方法的调用放在不同的方法里。

    

    方法:

    lock():将 Lock 实例锁定。若该 Lock 实例已被锁定,调用 lock() 方法的线程将会被阻塞,直到 Lock 实例解锁。

    lockInterruptblity() :将会被调用线程锁定,除非该线程被打断。此外,若一个线程在通过此方法来锁定 Lock 对象时进入阻塞等待,而它被打断了的话,该线程将会退出这个方法调用。

    tryLock():此方法试图立即锁定 Lock 实例。若锁定成功,它将返回 true,若 Lock 实例已被锁定,此方法返回 false。这已方法永不阻塞。tryLock(long timeout, TimeUnit timeunit)的工作类似与 tryLock 方法。除了它在放弃锁定 Lock 之前等待一个给定的超时时间外。

    unLock():此方法对 Lock 实例解锁。一个 Lock 实现将只允许锁定了该对象的线程来调用此方法。其他(没有锁定该 Lock 对象的线程)线程对 unLock() 方法的调用将会抛一个未检查异常(RuntimeException)。


20、读写锁(接口):ReadWriteLock

    一个先进的线程锁机制。能够允许多个线程在同一个时间对某一特定资源进行读取,但同一时间内只有一个线程对其进行写入。

    读写锁的理念在于对各线程能够对一个共享资源进行读取,而不会导致并发问题。

    并发问题的发生场景在于对一个共享资源的读和写的操作同时进行,或者多个写操作并发进行。


    ReadWriteLock 锁机制:

    一个线程在对保护资源在读和写之前对ReadWriteLockd的规则如下:

    读锁:若无任何写操作线程锁定 ReadWriteLock, 并且无任务写操作线程要求一个写锁(但还没有获得该锁),可以有多个读操作线程对该锁进行锁定。

    写锁:若无任何其他读操作或写操作,在写操作时,只有一个线程对该锁进行锁定。


    实现:ReentranReadWriteLock


21、原子类

    原子性布尔类型:AtomicBoolean

    原子性长整型:AtomicLong

    原子性引用性:AtomicReference

文章转载自释福,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论