接上一篇:
http://caoyaojun1988-163-com.iteye.com/blog/1302936
5 性能
虽然除了互斥锁,同步框架也支持其他许多风格的同步;但是锁的性能是最容易比较和衡量的,即便如此,还有很多不同的测量方法。这里的实验旨在揭示开销和吞吐量。在每项测试中,每个线程多次更新一个伪随机数,计算使用函数:nextRandom(INT种子):
int t = (seed % 127773) * 16807 –(seed / 127773) * 2836; return (t > 0)? t : t + 0x7fffffff;
其中一组线程每次迭代更新的概率为S,他们共享一个互斥锁下的生产器,另外一组线程使用无锁的本地生产器;并且在线程竞争的时候短时间的锁定区域,以减少其他因素的影响,使用随机数函数有两个方面的考虑,一是用于检测是否被锁定(它是一个很好生产器),二是让循环内的代码不会让JVM优化掉。
我们比较了4种锁:使用synchronized关键字的内建锁(Builtin)、在第4节演示的简单的互斥(Mutex)类、使用ReentrantLock的重入锁(Reentrant)、使用“公平”的模式的ReentrantLock的公平锁。所有的测试使用Sun J2SE1.5 JDK 的第46个build版本,选择服务器模式;在正式搜集数据之前,跑20组数据,以消除“预热”的影响;测试的时候,除了公平模式只迭代运行一百万次,其他每个线程迭代运行1000万次,
测试机器使用四个基于x86的机器和四个基于UltraSPARC的机器。所有x86机器上运行Linux RedHat2.4 NPTL的基代内核和库;所有的UltraSparc机器运行Solaris9。所有的测试都是在系统最轻负载时进行;测试机的其他方面没有要求,因为完全处于闲置状态;“4P”的名称,其实表示是两个超线程(hyperthreaded HT)Xeon的处理器,所以跟接近4核而不是2核。没有任何人为的缩小分歧。如下所示,相对应成本的同步器与处理器的个数、类型,速度并没有一个简单的关系。
5.1、负载
无竞争时的开销,可以通过仅仅运行一个线程,每次迭代用S=1的版本减去S=0的版本(不会去访问共享变量)的时间;表2显示了不同同步器的运行情况,单位是纳秒;Mutex类最接近测试框架的基本成本开销。重入锁(Reentrant )的额外开销,表示记录当前所有者的线程和错误检查的开销,公平锁的额外开销表示首先检查队列是否为空的开销。
表2还显示tryAcquire与使用“快速通道”--内置锁的成本。这里的差异主要反映了使用不同的原子操作和跨锁、机器的内存壁垒的成本。在多处理器时这些指标往往完全高于其他的。内置锁与同步器的主要的区别显然是由于Hotspot 的锁在获取锁和解锁都使用compareAndSet,而同步器的类只有在获取使用compareAndSet,释放的时候使用volatile 的写(即,屏蔽多处理器的内存屏障,和所有的处理器上的指令重排约束);
表3的场景是S=1、运行256个并发线程,制造大量的锁竞争,在完全饱和竞争的情况下,FIFO锁比内建锁有更小的开销(等价更大的吞吐量),并且比公平锁少两个数量级。这表明:高并发的情况下,允许闯入的FIFO的策略有更好的效率。
表3还显示,即使有较低的内部开销,但是由于上下文切换的时间导致了公平锁的较差的性能。上面的时间可以粗略的看到在各个不同平台上阻塞、唤醒线程的比例;此外,一个后续的实验(只使用机器4P)显示,公平性的设置对这些锁的总体方差影响不大。可以作为一个较粗粒度的变化维度,就线程结束时间而言,在4P的机器上公平锁(Fair)的标准差为0.7%,Reentrant的标准差为6.0%;作为对比,如果模拟长期持有锁,一个方案是,每个线程获取每个锁的时候计算16K的随机数
总运行时间几乎相同(公平锁9.79s,Reentrant9.72s);公平模式获取更小的标准差大约0.1%,而Reentrant 上升到29.5%。
5.2 吞吐
大多数的同步器使用时处于无争和饱和竞争的极端之间。这可以沿着两个维度去做实验和研究,一个是固定线程集合中线程的数量,改变发生竞争的概率,另一个是固定竞争的概率,增加线程集合中线程的数量。我们通过使用可重入锁(Reentrant),在不同的竞争概率和线程数量的场景中运行来说明这些因素的影响,下面是slowdown的计算方法:
t表示总共执行的时间, b是一个线程没有竞争或者同步时的执行时间,n是线程的数据,p是处理器的数量,s任然表示访问共享变量的概率;slowdown的值等于总时间除以最小的串行执行的时间加上并行执行的时间(这是理想情况);这是理想的时间模型,没有任何同步开销,因为没有任何线程与其他线程冲突而被阻塞。即便如此,在非常低的竞争下,一些测试结果可能比这个理想模型稍微好一些,,大概是由于jvm的优化,流水线等引起细微的差别;
图中所有的数字取2为低的对数,例如:1.0表示测试的时间是理想模型的2倍;4.0表示16倍;使用对数是为了解除对基本时间的依赖,所以与使用基础计算结果具有相同的趋势;这些测试使用的竞争的概率从1/128(0.008)到1;间隔2的幂,线程数是从1到1024,间隔是2的幂的一半;
在单处理器(1P和1U)的情况在,随着竞争的增加新能下降,但是跟线程的数量没有关系。多处理器一般会看到更糟糕的下降速度。多处理器的图形显示,在很少线程的时候会出现一个峰值,然后迅速下降;这反映了性能的过渡区,这个时候闯入的线程和被唤醒的线程同样可能获得锁,产生竞争因此经常迫使对方阻塞。在大多数情况下,在接下去就是平滑的区域,这个时候锁几乎永远不可用,导致跟单处理器的顺序的模式类似;无论有多少个处理器的机器,都必然出现。 同样还可以看到其他的信息;例如,全部有竞争的时候(概率为“1.000”)较少处理器的机器上slowdowns相对较差。
这些结果,可以发现如果调解阻塞(阻塞和唤醒)的支持方式去减少上下文切换和相关开销的可以提供很小但是对此框架有明显改善的性能。此外,可以使用其他形式的自适应的自旋、针对多个处理器的高并发的锁,可以避免这里看到的震动。而自适应自旋在不同的环境下工作往往是非常困难的,针对具体的应用程序可以使用配置文件去配置使用基于这个框架的自定义的锁。
6、总结:
截止写这篇文章的时候,java.util.concurrent中的同步框架还是很新的功能,还需要在实践中去评估,在j2se 1.5 发布之前,它的设计、实现、性能都是不可预知的;无论如何,框架的出现成功的满足可以基础它高效创建新的同步器的目标。
相关推荐
在 java.util.concurrent 多线程框架中,还提供了多种其他机制,包括并发集合、同步器、lock 等,以便开发者更方便地编写高效、可靠的多线程程序。并发集合提供了多种机制,包括 CopyOnWriteArrayList、...
Java.util.concurrent是Java 5.0引入的一个重要包,它为多线程编程提供了一组高级并发工具。这个包的设计者是Doug Lea,它的出现是JSR-166的一部分,也被称作Tiger更新。Java.util.concurrent的引入是为了解决传统...
`java.util.concurrent`包中的`AbstractQueuedSynchronizer`框架为Java开发者提供了一个强大的工具箱,使得他们能够高效地实现各种同步器。通过对同步状态的有效管理、阻塞和唤醒机制的优化以及灵活的扩展性设计,...
文档标题“java.util.concurrent同步器框架”和描述“Doug Lea的java.util.concurrent同步器框架”表明本文将探讨由Doug Lea所撰写的关于Java并发编程中同步器框架的内容。文档中提到了AbstractQueuedSynchronizer类...
AQS(AbstractQueuedSynchronizer)是Java.util.concurrent包中同步器的基础框架,它的核心设计思想与实现方法在Doug Lea先生的这篇论文中有详细的介绍。论文详细阐述了AQS框架的原理、设计、实现、应用以及性能等...
在Java并发编程中,`java.util.concurrent`(简称JUC)提供了丰富的类和接口,如Executor框架、线程池、并发集合、同步工具类等。这些工具使得程序员能够更方便地管理线程,避免了传统的锁和同步机制带来的复杂性和...
Java.util.concurrent(JUC)是Java平台中的一个核心包,专门用于处理多线程并发问题。这个包包含了大量的工具类和接口,极大地简化了并发编程的复杂性,提高了程序的性能和可伸缩性。本测试源文件主要是针对JUC并发...
总之,`java.util.concurrent` 提供的工具使得并发编程变得更加容易和高效,是 Java 并发编程的基石,无论是对于初学者还是经验丰富的开发者,理解和掌握这个包都是非常重要的。通过熟练运用这些工具,开发者可以...
然而,现代的多线程编程通常更倾向于使用并发集合,如`java.util.concurrent.CopyOnWriteArrayList`,它在读多写少的场景下有更好的性能。 6. **编程高手箴言** - 虽然`Vector`提供了线程安全,但其性能可能不满足...
在实际项目中,结合`java.util.concurrent`包与其他工具,比如Spring框架的ThreadPoolTaskExecutor,可以构建出高效、可扩展的并发解决方案。同时,持续学习和实践是提升并发编程能力的关键,不断探索新的并发模式和...
23. **`java.util.concurrent.locks.Lock`** 和 **`java.util.concurrent.locks.ReentrantLock`**: 锁机制,用于线程同步。 24. **`java.util.ArrayList`**: 用于创建堆栈、队列和双端队列的实现,如`ArrayDeque`。...
8. **Fork/Join框架**: `java.util.concurrent.ForkJoinPool` 和 `RecursiveTask` 或 `RecursiveAction` 用于并行执行可拆分任务,适合大量计算工作。 9. **ScheduledExecutorService**: 提供定时及周期性任务的...
`java.util.Queue`接口及其实现如ArrayDeque、LinkedList(作为Queue的实现),以及`java.util.concurrent`包下的并发队列如LinkedBlockingQueue、ConcurrentLinkedQueue等,提供高效的数据同步和处理机制。...
Java定时执行方法与节拍器是程序开发中常见的需求,特别是在服务器端应用或者后台服务中,需要定期执行某些任务,例如数据同步、日志清理、定时推送等。本篇文章将深入探讨Java中如何实现定时执行的方法,并介绍一个...
`Thread`类允许创建和管理独立执行的线程,而`java.util.concurrent`包包含了许多高级并发工具,如线程池、未来结果(Future)和同步器(Semaphore)。 4. **网络编程**:`java.net`包提供了网络通信的接口和类,如...
3. **同步容器**: `java.util.concurrent`包中的`BlockingQueue`接口及其实现(如`ArrayBlockingQueue`, `LinkedBlockingQueue`)是线程安全的数据结构,适用于生产者-消费者模型。此外,还有`ConcurrentHashMap`,...
- `java.util.concurrent` 包:包含线程池、并发容器、同步工具类,如`ExecutorService`、`CountDownLatch`、`CyclicBarrier`等。 7. **反射工具类**: - `java.lang.reflect` 包:提供反射API,可以在运行时动态...
Java并发编程是Java开发中的重要领域,特别是在多线程应用中,`java.util.concurrent`(JUC)包提供了丰富的工具类和接口,帮助开发者高效、安全地管理并发任务。本项目"Java_JUC_Study"显然是一个针对这个关键包的...