简单看看LongAccumulator
上篇博客我们看了AtomicLong和LongAdder的由来,但是有的时候我们想一下,LongAdder这个类也有点局限性,因为只能是每一次都+1,那有没有办法每次+2呢?或者每次乘以2?说得更抽象一点,我们能不能自己指定规则呢?干嘛老是傻乎乎的+1呢?
于是就有了LongAccumulator这个累加器,这个累加器更加抽象,前面使用的LongAdder只不过是这个累加器的一个特例,由此我们可以猜出这个累加器功能更加强大,但是需要我们自己的定制规则;
前提:看本篇博客的人应该熟悉jdk8中的函数式编程,jdk8是在2014年3月18日就推出了,到现在已经将近6年了,但是我们还是很多人在用着jdk8写着jdk7版本的代码,哎,无力吐槽!既然不能改变别人就改变自己吧!
一. 简单使用LongAccumulator累加器
我们先看看这个累加器的构成,如下所示:
public class LongAccumulator extends Striped64 implements Serializable {
//这是一个函数式接口,函数描述符是(T,T)->T ,两个相同类型的数据按照某种规则运算,返回相同类型的数据
private final LongBinaryOperator function;
//这个是累加器的初始值,也就是相当于LongAdder的base字段,就好像LongAdder的初始值是0一样,但是这里的累加器初始值可以不为0
private final long identity;
//可以看到构造函数中要传进去一个函数行为,还有初始值
public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity) {
this.function = accumulatorFunction;
base = this.identity = identity;
}
//这个就是函数式接口,用这个@FunctionalInterface注解标识,函数描述符是(T,T)->T
@FunctionalInterface
public interface LongBinaryOperator {
long applyAsLong(long left, long right);
}
下面我们就简单的使用了,看看LongAccumulator在实例化的时候,我们使用Lambda表达式指定了累加规则,其实就是将传进去的数据和初始值进行累加,而传进去的数字可以在accumulate方法里面动态指定!
package com.example.demo.study;
import java.util.concurrent.atomic.LongAccumulator;
public class Study0127 {
//这里使用LongAccumulator类
public LongAccumulator num = new LongAccumulator((a,b)->a+b, 0);
//每次调用这个方法,都会对全局变量加一操作,执行10000次
public void sum() {
for (int i = 0; i < 10000; i++) {
//LongAccumulator类的自增操作,这里的参数可以指定每次增加的数字
num.accumulate(1);
System.out.println("当前num的值为num= "+ num);
}
}
public static void main(String[] args) throws InterruptedException {
Study0127 demo = new Study0127();
//下面就是新建两个线程,分别调用一次sum方法
new Thread(new Runnable() {
@Override
public void run() {
demo.sum();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
demo.sum();
}
}).start();
}
}
如果我们每个线程都要对一个初始值乘以2,那么可以像下面这样修改,只需要修改lambda表达式,初始值还有线程每进行一次运算所需要的数字就行了,有兴趣的可以自己试试
....省略部分代码
//注意这里的lambda表达式
public LongAccumulator num = new LongAccumulator((a,b)->a*b, 1); //每次调用这个方法,都会对全局变量乘以2,每个线程都乘以4次
public void sum() {
for (int i = 1; i < 5; i++) {
//LongAccumulator类指定一个数字
num.accumulate(2);
System.out.println("当前num的值为num= "+ num);
}
}
.....省略部分代码

二.走进LongAccumulator
其实没什么特别想看的吧,就是很多的两个if语句那里一堆判断,注意这里的base是我们实例化LongAccumulator累加器时候传进去的初始值
第一个if语句,会尝试更新将初始值和每一步要计算的值运算返回结果,更新初始值,如果初始值更新完毕之后就不会往下走;如果初始值更新失败,那么往下走
第二个if语句,只有当找到了对应的Cell数组中的Cell元素用CAS更新值的时候失败才会进入到if里面
用上面的乘以2的例子说一下,一个线程,初始值base是1,乘以2运算,结果是2,此时使用CAS尝试将初始值base设置为2,如果成功那么当前线程执行完毕;设置失败(可能是其他线程已经更新了base的值了),那么就去找Cell数组中的某个Cell元素去更新它,更新成功,则线程执行完毕;更新失败,去初始化Cell数组、扩容Cell数组等操作了
public void accumulate(long x) {
Cell[] as; long b, v, r; int m; Cell a;
//注意这里这个if语句的第二个判断条件,这里就是传进去function.applyAsLong(b = base, x)),会调用我们传进去的lambda行为计算
if ((as = cells) != null || (r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null ||
!(uncontended = (r = function.applyAsLong(v = a.value, x)) == v || a.cas(v, r)))
//下面这个方法上次说了,就是对Cell数组的初始化,扩容和新创建Cell的操作,只不过在LongAdder中传递进去的function是null,
//而这里传进去的就是我们自己定义的lambda行为,就不进去看了,基本一样
longAccumulate(x, function, uncontended);
}
}
//用CAS更新base的值
final boolean casBase(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
}
这个longAccumulate()方法上一篇已经分析过了,就不多说了,唯一不同的就是这次的fn不为null,所以这里不是v+x,而是fn.applyAsLong(v, x),也就是我们传进去的lambda表达式

这次这个类没什么新的东西吧,就是用到了jdk8的行为参数化,我们传进去的不再是一个数值或者字符串,更应该说是传进去一个函数,熟悉js的人应该知道,有兴趣的可以了解一下!
简单看看LongAccumulator的更多相关文章
- Java并发编程笔记之LongAdder和LongAccumulator源码探究
一.LongAdder原理 LongAdder类是JDK1.8新增的一个原子性操作类.AtomicLong通过CAS算法提供了非阻塞的原子性操作,相比受用阻塞算法的同步器来说性能已经很好了,但是JDK ...
- 【造轮子】打造一个简单的万能Excel读写工具
大家工作或者平时是不是经常遇到要读写一些简单格式的Excel? shit!~很蛋疼,因为之前吹牛,就搞了个这东西,还算是挺实用,和大家分享下. 厌烦了每次搞简单类型的Excel读写?不怕~来,喜欢流式 ...
- Fabio 安装和简单使用
Fabio(Go 语言):https://github.com/eBay/fabio Fabio 是一个快速.现代.zero-conf 负载均衡 HTTP(S) 路由器,用于部署 Consul 管理的 ...
- node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理
一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...
- 哪种缓存效果高?开源一个简单的缓存组件j2cache
背景 现在的web系统已经越来越多的应用缓存技术,而且缓存技术确实是能实足的增强系统性能的.我在项目中也开始接触一些缓存的需求. 开始简单的就用jvm(java托管内存)来做缓存,这样对于单个应用服务 ...
- 在Openfire上弄一个简单的推送系统
推送系统 说是推送系统有点大,其实就是一个消息广播功能吧.作用其实也就是由服务端接收到消息然后推送到订阅的客户端. 思路 对于推送最关键的是服务端向客户端发送数据,客户端向服务端订阅自己想要的消息.这 ...
- 我的MYSQL学习心得(一) 简单语法
我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...
- 使用 Nodejs 搭建简单的Web服务器
使用Nodejs搭建Web服务器是学习Node.js比较全面的入门教程,因为要完成一个简单的Web服务器,你需要学习Nodejs中几个比较重要的模块,比如:http协议模块.文件系统.url解析模块. ...
- ASP.NET Aries 入门开发教程2:配置出一个简单的列表页面
前言: 朋友们都期待我稳定地工作,但创业公司若要躺下,也非意念可控. 若人生注定了风雨飘摇,那就雨中前行了. 最机开始看聊新的工作机会,欢迎推荐,创业公司也可! 同时,趁着自由时间,抓紧把这系列教程给 ...
随机推荐
- 黑马客户管理系统(SSM)
黑马客户管理系统 1系统概述 1.1系统功能介绍 本系统后台使用SSM框架编写,前台页面使用当前主流的Bootstrap和jQuery框架完成页面信息展示功能(关于Bootstrap的知识,有兴趣的读 ...
- 073、Java面向对象之利用构造方法为属性赋值
01.代码如下: package TIANPAN; class Book { // 定义一个新的类 private String title; // 书的名字 private double price ...
- python三大神器===》迭代器
迭代器: 1.认识迭代器 迭代器是访问集合元素的一种方式.迭代器是一个可以记住遍历的位置的对象.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 我们怎样才能 ...
- 快速为Eclipse配置PyDev插件
想学习Python,查询网络之后发现PyDev是很好的插件,所以就想为Eclipse配置它.结果在整个配置的过程中出现了各种问题,版本问题,重复问题,反正乱七八糟的,本身安装一次的时间就很长,中间出现 ...
- '/'和‘/*’差异造成的No mapping found for HTTP request with URI [/springMVC/welcome.jsp] in DispatcherServlet with name 'springmvc'
在采用springMVC框架的时候所遇到的一个小问题,其中web.xml中关于servlet的配置如下: <servlet> <servlet-name>springmvc&l ...
- 值得一学的C语言
P1085 不高兴的津津 #include <stdio.h> int main( ) { int a,b; int max=0; int result; for (int i = 0; ...
- HTML<figure> <figcaption> 标签定义图文并茂
本来想分两篇文章来解释说明figure.figcaption的,但是这俩个标签都是定义图文的,所以我们合起来讲解,大家更能容易接受. 大家在写xhtml.html中常常用到一种图片列表,图片+标题 或 ...
- UVA 12510/CSU 1119 Collecting Coins DFS
前年的省赛题,难点在于这个石头的推移不太好处理 后来还是看了阳神当年的省赛总结,发现这个石头这里,因为就四五个子,就暴力dfs处理即可.先把石头当做普通障碍,进行一遍全图的dfs或者bfs,找到可以找 ...
- pig安装配置及实例
一.前提 1. hadoop集群环境配置好(本人hadoop版本:hadoop-2.7.3) 2. windows基础环境准备: jdk环境配置.esclipse环境配置 二.搭建pig环境 1.下载 ...
- C++学习记录——(queue的清空)
c++自带的queue并没有clear这个方法:所以只能自己写了. 一共三种(其实我决得就是两种): 第一种: 直接赋值 queue<int> MyQue; /* …… */ MyQue ...