引言:
并发处理在实际开发中应用场景还是挺多了,查阅了一些资料,对一些并发常用类、方法做了简单汇总,希望对大家有所帮助!
正文:
1. AtomicLong
在 Java 语言中,协调对共享字段的访问的传统方法是使用同步,确保完成对共享字段的所有访问,同时具有适当的锁定。通过同步,可以确定(假设类编写正确)具有保护一组给定变量的锁定的所有线程都将拥有对这些变量的独占访问权,并且以后其他线程获得该锁定时,将可以看到对这些变量进行的更改。弊端是如果锁定竞争太厉害(线程常常在其他线程具有锁定时要求获得该锁定),会损害吞吐量,因为竞争的同步非常昂贵。(Public Service Announcement:对于现代 JVM 而言,无竞争的同步现在非常便宜。
比较并交换(CAS)可以有效解决这个问题.
支持并发的第一个处理器提供原子的测试并设置操作,通常在单位上运行这项操作。现在的处理器(包括 Intel 和 Sparc 处理器)使用的最通用的方法是实现名为 比较并转换或 CAS 的原语。(在 Intel 处理器中,比较并交换通过指令的 cmpxchg 系列实现。PowerPC 处理器有一对名为“加载并保留”和“条件存储”的指令,它们实现相同的目地;MIPS 与 PowerPC 处理器相似,除了第一个指令称为“加载链接”。)
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”
通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。
类似于 CAS 的指令允许算法执行读-修改-写操作,而无需害怕其他线程同时修改变量,因为如果其他线程修改变量,那么 CAS 会检测它(并失败),算法可以对该操作重新计算。CAS 的价值是它可以在硬件中实现,并且是极轻量级的(在大多数处理器中)
java.util.concurrent.atomic
包中所有原子变量类都公开比较并设置原语(与比较并交换类似),这些原语都是使用平台上可用的最快本机结构(比较并交换、加载链接/条件存储,最坏的情况下是旋转锁)来实现的。 java.util.concurrent.atomic
包中提供了原子变量的 9 种风格( AtomicInteger
; AtomicLong
; AtomicReference
; AtomicBoolean
;原子整型;长型;引用;及原子标记引用和戳记引用类的数组形式,其原子地更新一对值)
表面看起来与Synchronized同步方法类似,但底层原子变量的操作会变为平台提供的用于并发访问的硬件原语,比如比较并交换。
参考资料:
http://www.gznc.edu.cn/yxsz/jjglxy/book/Java_api/java/util/concurrent/atomic/AtomicLong.html
http://www.ibm.com/developerworks/cn/java/j-jtp11234/
http://www.ibm.com/developerworks/cn/education/java/j-concur/section7.html
2. BlockingQueue
阻塞队列,BlockingQueue
干净利落地解决了如何将一个线程收集的项“传递”给另一线程用于处理的问题,无需考虑同步问题。
BlockingQueue
接口表示它是一个 Queue
,意思是它的项以先入先出(FIFO)顺序存储。在特定顺序插入的项以相同的顺序检索 — 但是需要附加保证,从空队列检索一个项的任何尝试都会阻塞调用线程,直到这个项准备好被检索。同理,想要将一个项插入到满队列的尝试也会导致阻塞调用线程,直到队列的存储空间可用。
参考资料:
http://www.ibm.com/developerworks/cn/java/j-5things4.html
http://www.ibm.com/developerworks/cn/java/j-5things5.html
3. semaphone
可以限制未处理的特定资源请求(线程/操作)数量,事实上,限制有时候能够提高系统的吞吐量,因为它们减少了对特定资源的争用。
4.CountDownLatch
CountDownLatch
就像是赛马场的起跑门栅。此类持有所有空闲线程,直到满足特定条件,这时它将会一次释放所有这些线程。
await: 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断
countdown:递减锁存器的计数,如果计数到达零,则释放所有等待的线程
参考资料:
http://www.cjsdn.net/doc/jdk50/java/util/concurrent/CountDownLatch.html
http://hi.baidu.com/chenwei6111/blog/item/168715272ebd2f0f908f9d6b.html
5.Executor
创建 Thread
是一项重量型的操作,重用现有 Thread
比创建新线程要容易得多。
Executor exec = getAnExecutorFromSomeplace();
exec.execute(new Runnable() { ... });
6.ExecutorService
是对Executor的扩展,比如结束一个用于生成结果的线程并以非阻塞方式等待结果可用。(这是桌面应用程序的一个常见需求,用户将执行需要访问数据库的 UI 操作,然后如果该操作花费了很长时间,可能希望在它完成之前取消它。)
ExecutorService
可以接受一组任务并返回一个表示每项任务的未来结果的未来列表。
ScheduledExecutorService
的应用范围,它扩展了 ExecutorService
。java.util.concurrent
库相比起早期并发特性的一大进步,比如监控锁定。