漫谈并发编程(六):java中一些常用的并发构件的介绍

CountDownLatch

它被用来同步一个或多个任务,强制它们等待其它任务执行的一组操作完成。

你可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用await()的方法都将阻塞,直至这个计数值到达0。其它任务在结束其工作时,可以在该对象上调用countDown()来减小这个计数值。CountDownLatch被设计为只触发一次,计数值不能被重置。如果你需要能够重置值的版本,则可以使用CylicBarrier。

调用countDown()的任务在产生这个调用时并没有被阻塞,只有对await()的调用会被阻塞,直至计数值到达0。

CountDownLatch的典型用法是当前程序有n个互相独立的可解决任务,并创建值为n的CountDownLatch。当每个任务完成时,都会在这个锁存器上调用countDown()。等待问题被解决的任务在这个锁存器上调用await(),将它们自己拦住,直到锁存器计数结束。

总结

CountDownLatch模拟了一种多任务阻塞等待-多可解决任务达成某一条件的这一场景。如果只是多任务阻塞等待某一任务,那么直接使用wait和signalAll就可以简单实现了。如果是等待多任务完成,使用wait和signalAll就必须要结合一个计数值及锁来共同实现这种情景。java帮我们抽象出了这种应用场景,解决方案名为CountDownLatch。

CyclicBarrier

CyclicBarrier适用于这样的情况:你希望创建一组任务,它们并行的执行工作,然后在进行下一个步骤之前等待,直至所有任务都完成(看起来有些像join())。它使得所有的并行任务都将在栅栏处队列,因此可以一致地向前移动。

总结

CountDownLatch像有裁判员的田径比赛,要等所有裁判员都到位之后,才能开跑。CyclicBarrier像没有裁判员的比赛,只需要比赛队员各就各位之后就可以直接开跑。试想一下用wait和signal如何实现这种场景,利用一个锁来控制一个计数值的访问,如果这个计数值大于1,就wait到某个对象上(可以是这个计数值对象),然后-1,如果计数值为1,则对该对象signalAll。java帮我们抽象出这种场景,名曰CyclicBarrier。

DelayQueue

这是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取出。这种队列是有序的,即队头对象的延迟到期的时间最长。如果没有任何延迟到期,那么就不会有任何头元素,并且poll()将返回null(正因为这样,你不能将null放置到这种队列中)。

Delayed接口有一个方法名为getDelay(),它可以用来告知延迟到期有多长时间,或者延迟在多长时间之前已经到期,这个方法将限制我们去使用TimeUnit类。

总结

对于普通队列来说就是一个先进先出的队列。对于DelayQueue队列的理解可以认为它存在两个队列,一个优先级队列存放还未到期的任务(从队头到队尾的任务,根据到期时间由近到远排序),然后依次将到期的任务放入一个有序队列,这个有序队列就是我们真正take时取的队列。该构件如其名,代表放入该队列的对象自己可以控制自己被取出的时间。

在使用场景上,DelayQueue适用于限制在多少时间之后才执行的任务。这里限制它的是时间,而不是其他线程。

PriorityBlockingQueue

与普通BlockingQueue区别仅在于不以进队列时间评出队列时间,而以优先级高低说了算。一个以时间论英雄,一个以优先级论英雄。

SynchronousQueue

这是一种没有内部容量的阻塞队列,因此每个put()都必须等待一个take(),反之亦然。这就好像是你把一个对象交给某人-没有任何桌子可以放置这个对象,因此只有在这个人伸出手,准备好接收这个对象时,你才能工作。

ScheduledExecutor

ScheduledExecutor提供给你一种可以定期启动线程的方式。你可以使用schedule()(运行一次任务)或者ScheduleAtFixedRate()(每隔规则的时间重复执行任务),你可以将Runnable对象设置为在将来的某个时刻执行。

功能上类似于定时器,在web系统中,使用非常频繁。

Semaphore

正常的锁(来自concurrent.locks或内建的synchronized锁),在任何时刻都只允许一个任务访问一项资源,而计数信号量允许n个任务同时访问这个资源。

实际上,信号量使用较少:1. 资源访问大多只存在1到2的区别,不存在2到多的区别,线程安全的对象多个访问时没有问题,线程不安全的对象两个线程访问就有问题。 2. 如果当前使用场景是只有n个对象提供服务,可以用信号量实现,实际上用阻塞队列可能是更好的做法。

Exchanger

Exchanger是在两个任务之间交换对象的栅栏。当这些任务进入栅栏时,它们各自拥有一个对象,当它们离开时,它们都拥有之前由对象持有的对象。Exchanger的典型应用场景是:一个任务在创建对象,这些对象的生产代价很高昂,而另一个任务在消费这些对象。通过这种方式,可以有更多的对象在创建的同时被消费。

时间: 2024-10-11 12:14:20

漫谈并发编程(六):java中一些常用的并发构件的介绍的相关文章

Java并发编程:Java中的锁和线程同步机制

锁的基础知识 锁的类型 锁从宏观上分类,只分为两种:悲观锁与乐观锁. 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作.Java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败. 悲观

java中最常用jar包的用途说明

java中最常用jar包的用途说明,适合初学者 jar包 用途 axis.jar SOAP引擎包 commons-discovery-0.2.jar 用来发现.查找和实现可插入式接口,提供一些一般类实例化.单件的生命周期管理的常用方法. jaxrpc.jar Axis运行所需要的组件包 saaj.jar 创建到端点的点到点连接的方法.创建并处理SOAP消息和附件的方法,以及接收和处理SOAP错误的方法.   wsdl4j-1.5.1.jar Axis运行所需要的组件包 activation.ja

为什么函数式编程在Java中很危险?

摘要:函数式编程这个不温不火的语言由来已久.有人说,这一年它会很火,尽管它很难,这也正是你需要学习的理由.那么,为什么函数式编程在Java中很危险呢?也许这个疑问普遍存在于很多程序员的脑中,作者Elliotte对此发表了一些见解,我们一起来看看他是怎么说的. 在我的日常工作中,我身边的开发者大多是毕业于CS编程顶级院校比如MIT.CMU以及Chicago,他们初次涉及的语言是Haskell.Scheme及Lisp.他们认为函数式编程是一种自然的.直观的.美丽的且高效的编程样式.但奇怪的是,我和我

Java中常见的5种WEB服务器介绍

这篇文章主要介绍了Java中常见的5种WEB服务器介绍,它们分别是Tomcat.Resin.JBoss.WebSphere.WebLogic,需要的朋友可以参考下 Web服务器是运行及发布Web应用的容器,只有将开发的Web项目放置到该容器中,才能使网络中的所有用户通过浏览器进行访问.开发Java Web应用所采用的服务器主要是与JSP/Servlet兼容的Web服务器,比较常用的有Tomcat.Resin.JBoss.WebSphere 和 WebLogic 等,下面将分别进行介绍. Tomc

《java并发编程实战》读书笔记4--基础构建模块,java中的同步容器类&并发容器类&同步工具类,消费者模式

上一章说道委托是创建线程安全类的一个最有效策略,只需让现有的线程安全的类管理所有的状态即可.那么这章便说的是怎么利用java平台类库的并发基础构建模块呢? 5.1 同步容器类 包括Vector和Hashtable,此外还包括在JDK1.2中添加的一些功能相似的类,这些同步的封装器类由Collections.synchronizedXxx等工厂方法创建的.这些类实现线程安全的方式是:将他们的状态封装起来,并对每个共有方法都进行同步,使得每次只能有一个线程能访问容器的状态. 关于java中的Vect

Java并发编程(六) 一个日志服务的例子

日志服务需要提供的功能有: 可以从外部安全地开启和关闭日志服务: 可以供多个线程安全地记录日志消息: 在日志服务关闭后,可以把剩余未记录的消息写入日志文件: public class LogService { private final BlockingQueue<String> msgQueue; //阻塞的消息队列保存日志消息 private final PrintWrite writer; //写消息到日志文件 private final LoggerThread logThread;

多线程编程学习六(Java 中的阻塞队列).

介绍 阻塞队列(BlockingQueue)是指当队列满时,队列会阻塞插入元素的线程,直到队列不满:当队列空时,队列会阻塞获得元素的线程,直到队列变非空.阻塞队列就是生产者用来存放元素.消费者用来获取元素的容器. 当线程 插入/获取 动作由于队列 满/空 阻塞后,队列也提供了一些机制去处理,或抛出异常,或返回特殊值,或者线程一直等待... 方法/处理方式 抛出异常 返回特殊值 一直阻塞 超时退出 插入方法 add(e) offer(e) put(e) offer(e, timeout, unit

Java并发编程(3) JUC中的锁

一 前言 前面已经说到JUC中的锁主要是基于AQS实现,而AQS(AQS的内部结构 .AQS的设计与实现)在前面已经简单介绍过了.今天记录下JUC包下的锁是怎么基于AQS上实现的 二 同步锁 同步锁不是JUC中的锁但也顺便提下,它是由synchronized 关键字进行同步,实现对竞争资源互斥访问的锁. 同步锁的原理:对于每一个对象,有且仅有一个同步锁:不同的线程能共同访问该同步锁.在同一个时间点该同步锁能且只能被一个线程获取到,其他线程都得等待. 另外:synchronized是Java中的关

Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析

目录 引出线程池 Executor框架 ThreadPoolExecutor详解 构造函数 重要的变量 线程池执行流程 任务队列workQueue 任务拒绝策略 线程池的关闭 ThreadPoolExecutor创建线程池实例 参考: 引出线程池 线程是并发编程的基础,前面的文章里,我们的实例基本都是基于线程开发作为实例,并且都是使用的时候就创建一个线程.这种方式比较简单,但是存在一个问题,那就是线程的数量问题. 假设有一个系统比较复杂,需要的线程数很多,如果都是采用这种方式来创建线程的话,那么