转自:http://www.importnew.com/11345.html

我只是喜欢新鲜的事物,而Java 8 有很多新东西。这次我想讨论其中我最喜欢的之一:并发加法器。这是一个新的类集合,他们用来管理被多线程读写的计数器。这个新的API在显著提升性能同时,仍然保持了简单直接的特点。

多核架构到来之后人们就解决着并发计数器,让我们来看看到现在为止Java提供了哪些解决并发计数器的选项,并对比一下他们与新API的性能。

脏计数器 – 这种方法意味着一个常规对象或静态属性正在被多线程读写。不幸的是,由于两个原因这行不通。原因之一,在Java中A += B操作不是原子的。如果你打开输出字节码,你将至少看到四个指令 —— 第一个用来将属性值从堆加载到线程栈,第二个用来加载delta,第三个用来把它们相加,第四个用来将结果重新分配给属性值。

如果多个线程同时作用于同一块内存单元,写操作有很大机会丢失,因为一个线程可以覆盖另一个线程的值(又名“读-修改-写”),另一个令人不快的是这种情况下你不得不处理值的冲突,还有更坏的情况。

这是相当菜鸟的一个问题,而且超级难调试。如果你确实发现有人在你的应用中这么做的话,我想要你帮个小忙。在你的数据库中搜索“Tal Weiss”,如果存在我的记录,请删除,这样我会觉得安全些。

Synchronized – 最基本的并发用语,它在读写一个值的时候会阻塞所有想读写该值的其他线程。虽然它是可行的,但你的代码却注定要被转向DMV line

读写锁 – 基本Java锁的略复杂版本,它使你能够区分修改值并且需要阻塞其他线程的线程和仅是读取值并且不需要临界区的线程。虽然这更有效率(假设写线程数量很 少),但由于当你获取写锁的时候阻塞了所有其他线程的执行,这真是一个“漂亮”的方法。事实上,只有当你了解到相比读线程,写线程的数量极大地受限时它才 真正是一个好方法。

Volatile – 这个关键词非常容易被误解,它指示JIT编译器重新优化运行时机器码,使得属性的任何修改对其他线程都是即时可见的。

这将导致一些JIT处理内存分配的顺序这项JIT编译器最喜爱的优化失效。你再说一遍?是的,你没有听错。JIT编译器可以改变属性分配的顺序。这个神秘的小策略(又叫happens-before)能够最小化程序访问全局堆的次数,同时仍然确保你的代码没有被影响。真是相当隐蔽…

所以什么时候应该使用volatile处理计数器呢?如果你仅有一个线程更新值并且多个线程读取它,这时使用volatile无疑是一个真正好的策略。

那为什么不总是使用它呢?因为当多个线程同时更新属性的时候它不能很好的工作。由于A += B不是原子操作,这将带来覆盖其他写操作的风险。在Java8之前,处理这种情况你需要使用的是AtomicInteger。

AtomicInteger – 这组类使用CAS(比较并交换)处理器指令来更新计数器的值。听起来不错,真的是这样吗?是也不是。好的一面是它通过一个直接机器码指令设置值时,能够最 小程度地影响其他线程的执行。坏的一面是如果它在与其他线程竞争设置值时失败了,它不得不再次尝试。在高竞争下,这将转化为一个自旋锁,线程不得不持续尝 试设置值,无限循环直到成功。这可不是我们想要的方法。让我们进入Java 8的LongAdders。

Java 8 加法器 – 这是一个如此酷的新API以至于我一直在滔滔不绝地谈论它。从使用的角度看它与AtomicInteger非常相似,简单地创建一个LongAdder实例,并使用intValue()和add()来获取和设置值。神奇的地方发生在幕后。

这个类所做的事情是当一个直接CAS由于竞争失败时,它将delta保存在为该线程分配的一个内部单元对象中,然后当intValue()被调用时,它会将这些临时单元的值再相加到结果和中。这就减少了返回重新CAS或者阻塞其他线程的必要。多么聪明的做法!

好吧,已经说的够多了-让我们看看这个类的实际表现吧。我们设立了下面的基准测试-通过多线程将一个计数器增加到10^8。我们用总共10个线程来运行这个测试-5个写操作,5个读操作。测试机器仅有一个四核的i7处理器,因此测试一定会产生一些严重的竞争:

代码这里可以下载到

注意dirty和volatile都冒着一些严重的值覆盖危险。

总结

  • 并行加法器相比原子整数拥有60%-100%的性能提升
  • 执行加法的线程之间没有太大差别,除非被锁定
  • 注意当你使用synchronized或读写锁时所带来的巨大性能问题 – 慢一个甚至两个数量级

我非常愿意听到-你已经有机会在你的代码中使用这些类了。

Java 8 LongAdders:管理并发计数器的正确方式的更多相关文章

  1. Java并发计数器探秘

    前言 一提到线程安全的并发计数器,AtomicLong 必然是第一个被联想到的工具.Atomic* 一系列的原子类以及它们背后的 CAS 无锁算法,常常是高性能,高并发的代名词.本文将会阐释,在并发场 ...

  2. Java系列笔记(6) - 并发(上)

    目录 1,基本概念 2,volatile 3,atom 4,ThreadLocal 5,CountDownLatch和CyclicBarrier 6,信号量 7,Condition 8,Exchang ...

  3. Java自动内存管理机制学习(一):Java内存区域与内存溢出异常

    备注:本文引用自<深入理解Java虚拟机第二版> 2.1 运行时数据区域 Java虚拟机在执行Java程序的过程中把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创 ...

  4. Java 面试宝典!并发编程 71 道题及答案全送上!

    金九银十跳槽季已经开始,作为 Java 开发者你开始刷面试题了吗?别急,我整理了71道并发相关的面试题,看这一文就够了! 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程( ...

  5. Java的内存管理机制之内存区域划分

    各位,好久不见.先做个预告,由于最近主要在做Java服务端开发,最近一段时间会更新Java服务端开发相关的一些知识,包括但不限于一些读书笔记.框架的学习笔记.和最近一段时间的思考和沉淀.先从Java虚 ...

  6. 1 Java内存区域管理

    目录 1 关于自动内存管理 2 运行时数据区域 2.1 程序计数器 2.2 虚拟机栈 2.2.1 局部变量表 2.2.2 操作数栈 2.3 本地方法栈 2.4 堆 2.5 方法区 2.5.1 运行时常 ...

  7. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  8. Java中不同的并发实现的性能比较

    Fork/Join框架在不同配置下的表现如何? 正如即将上映的星球大战那样,Java 8的并行流也是毁誉参半.并行流(Parallel Stream)的语法糖就像预告片里的新型光剑一样令人兴奋不已.现 ...

  9. Java是如何管理内存的?

    本文转自CSDN用户Kevin涂腾飞的文章java内存管理机制:http://blog.csdn.net/tutngfei1129287460/article/details/7383480 JAVA ...

随机推荐

  1. 存储过程分页 Ado.Net分页 EF分页 满足90%以上

    存储过程分页: create proc PR_PagerDataByTop @pageIndex int, @pageSize int, @count int out as select top(@p ...

  2. eclipse中 properties文件编码问题

    1. Eclipse修改设置 项目中用到了配置文件,所以在Eclipse中新建.properties文件,文件中编辑了中文,在保存时Eclipse报出以下错误: 解决这个问题的方法: 依次选择: 菜单 ...

  3. Struts 2常用的Ajax标签

    Struts 2对Ajax的支持 •Struts 2对Ajax提供了很好的支持 –Struts 2.1提供了基于Dojo的Ajax标签,对Ajax操作进行了进步封装,可以更快捷容易的使用Ajax   ...

  4. 由node-webkit想到

    本人做为.NET的死忠也有些许年头.微软这几年被谷歌苹果之流打的有点招架不住..NET的前景也难免堪忧.虽然我认为就强类型语言方面,C#绝对是最强者.但是新技术的发展确实是可怕的,看看苹果几年就把no ...

  5. 利用Aspose.Cell控件导入Excel非强类型的数据

    导入Excel的操作是非常常见的操作,可以使用Aspose.Cell.APOI.MyXls.OLEDB.Excel VBA等操作Excel文件,从而实现数据的导入,在导入数据的时候,如果是强类型的数据 ...

  6. Python基础:新式类的属性访问

    一.概述 二.准备工作 1.讨论对象 2.名词解释 三.实例绑定的属性访问 1.获取属性 一般规则 参考源码 示例验证 2.设置属性 一般规则 参考源码 示例验证 3.删除属性 一般规则 参考源码 示 ...

  7. cmd命令行编译和运行java程序报错 NoClassDefFoundError

    首先,当在运行java程序出现这个错误时,首先考虑是不是路径或者未指定正确的包名,例如当运行下面这个文件时: package cn.wgh.socket; public class HelloWorl ...

  8. 【poj 2185】Milking Grid(字符串--KMP+问题分解)

    题意:给定一个由字符组成的矩阵,求出它的面积最小的覆盖矩阵.(感觉应该是可重叠的......* (・ω・)っ) 解法:KMP.行列互不影响,可以问题分解.先求出每一行的最小重复串,利用kmp中的nex ...

  9. 不变(Invariant), 协变(Covarinat), 逆变(Contravariant) : 一个程序猿进化的故事

    阿袁工作的第1天: 不变(Invariant), 协变(Covarinat), 逆变(Contravariant)的初次约 阿袁,早!开始工作吧. 阿袁在笔记上写下今天工作清单: 实现一个scala类 ...

  10. mysql root强密码的必要性max_allowed_packet被改成1024引起的风险

    前两天运维反馈说,有些机器的max_allowed_packet隔两天就会被改成1024,导致客户端调用时出错,网上有说内存不够的,也有人工修改的. 运维小姑娘一口咬定肯定没有改过的,而且my.cnf ...