Java 8 并发: 原子变量和 ConcurrentMap

原文地址: Java 8 Concurrency Tutorial: Atomic Variables and ConcurrentMap

AtomicInteger

java.concurrent.atomic 包下有很多原子操作的类。 在有些情况下,原子操作可以在不使用 synchronized 关键字和锁的情况下解决多线程安全问题。

在内部,原子类大量使用 CAS, 这是大多数现在 CPU 支持的原子操作指令, 这些指令通常情况下比锁同步要快得多。如果需要同时改变一个变量, 使用原子类是极其优雅的。

现在选择一个原子类 AtomicInteger 作为例子

AtomicInteger atomicInt = new AtomicInteger(0);ExecutorService executor = Executors.newFixedThreadPool(2);IntStream.range(0, 1000).forEach(i -> executor.submit(atomicInt::incrementAndGet));stop(executor);System.out.println(atomicInt.get());    // => 1000复制代码

使用 AtomicInteger 代替 Integer 可以在线程安全的环境中增加变量, 而不要同步访问变量。incrementAndGet() 方法是一个原子操作, 我们可以在多线程中安全的调用。

AtomicInteger 支持多种的原子操作, updateAndGet() 方法接受一个 lambda 表达式,以便对整数做任何的算术运算。

AtomicInteger atomicInt = new AtomicInteger(0);ExecutorService executor = Executors.newFixedThreadPool(2);IntStream.range(0, 1000).forEach(i -> {Runnable task = () ->atomicInt.updateAndGet(n -> n + 2);executor.submit(task);});stop(executor);System.out.println(atomicInt.get());    // => 2000
复制代码

accumulateAndGet() 方法接受一个 IntBinaryOperator 类型的另一种 lambda 表达式, 我们是用这种方法来计算 1 -- 999 的和:

AtomicInteger atomicInt = new AtomicInteger(0);ExecutorService executor = Executors.newFixedThreadPool(2);IntStream.range(0, 1000).forEach(i -> {Runnable task = () ->atomicInt.accumulateAndGet(i, (n, m) -> n + m);executor.submit(task);});stop(executor);System.out.println(atomicInt.get());    // => 499500
复制代码

还有一些其他的原子操作类: AtomicBoolean AtomicLong AtomicReference

LongAdder

作为 AtomicLong 的替代, LongAdder 类可以用来连续地向数字添加值。

ExecutorService executor = Executors.newFixedThreadPool(2);IntStream.range(0, 1000).forEach(i -> executor.submit(adder::increment));stop(executor);System.out.println(adder.sumThenReset());   // => 1000
复制代码

LongAdder 类和其他的整数原子操作类一样提供了 add()increment() 方法, 同时也是线程安全的。但其内部的结果不是一个单一的值, 这个类的内部维护了一组变量来减少多线程的争用。实际结果可以通过调用 sum()sumThenReset() 来获取。

当来自多线程的更新比读取更频繁时, 这个类往往优于其他的原子类。通常作为统计数据, 比如要统计 web 服务器的请求数量。 LongAdder 的缺点是会消耗更多的内存, 因为有一组变量保存在内存中。

LongAccumulator

LongAccumulatorLongAdder 的一个更通用的版本。它不是执行简单的添加操作, 类 LongAccumulator 围绕 LongBinaryOperator 类型的lambda表达式构建,如代码示例中所示:

LongBinaryOperator op = (x, y) -> 2 * x + y;
LongAccumulator accumulator = new LongAccumulator(op, 1L);ExecutorService executor = Executors.newFixedThreadPool(2);IntStream.range(0, 10).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));stop(executor);System.out.println(accumulator.getThenReset());     // => 2539
复制代码

我们使用函数 2 * x + y 和初始值1创建一个 LongAccumulator。 每次调用 accumulate(i) , 当前结果和值i都作为参数传递给``lambda` 表达式。

LongAdder 一样, LongAccumulator 在内部维护一组变量以减少对线程的争用。

ConcurrentMap

ConcurrentMap 接口扩展了 Map 接口,并定义了最有用的并发集合类型之一。 Java 8 通过向此接口添加新方法引入了函数式编程。

在下面的代码片段中, 来演示这些新的方法:

ConcurrentMap<String, String> map = new ConcurrentHashMap<>();
map.put("foo", "bar");
map.put("han", "solo");
map.put("r2", "d2");
map.put("c3", "p0");
复制代码

forEach() 接受一个类型为 BiConsumerlambda 表达式, 并将 mapkeyvalue 作为参数传递。

map.forEach((key, value) -> System.out.printf("%s = %s\n", key, value));
复制代码

putIfAbsent() 方法只有当给定的 key 不存在时才将数据存入 map 中, 这个方法和 put 一样是线程安全的, 当多个线程访问 map 时不要做同步操作。

String value = map.putIfAbsent("c3", "p1");
System.out.println(value);    // p0
复制代码

getOrDefault() 方法返回给定 keyvalue, 当 key 不存在时返回给定的值。

String value = map.getOrDefault("hi", "there");
System.out.println(value);    // there
复制代码

replaceAll() 方法接受一个 BiFunction 类型的 lambda 表达式, 并将 keyvalue 作为参数传递,用来更新 value

map.replaceAll((key, value) -> "r2".equals(key) ? "d3" : value);
System.out.println(map.get("r2"));    // d3
复制代码

compute() 方法和 replaceAll() 方法有些相同, 不同的是它多一个参数, 用来更新指定 keyvalue

map.compute("foo", (key, value) -> value + value);
System.out.println(map.get("foo"));   // barbar
复制代码

ConcurrentHashMap

以上所有方法都是 ConcurrentMap 接口的一部分,因此可用于该接口的所有实现。 此外,最重要的实现 ConcurrentHashMap 已经进一步增强了一些新的方法来在 Map 上执行并发操作。

就像并行流一样,这些方法在 Java 8 中通过 ForkJoinPool.commonPool()提供特殊的 ForkJoinPool 。该池使用预设的并行性, 这取决于可用内核的数量。 我的机器上有四个CPU内核可以实现三种并行性:

System.out.println(ForkJoinPool.getCommonPoolParallelism());  // 3
复制代码

通过设置以下 JVM 参数可以减少或增加此值:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
复制代码

我们使用相同的示例来演示, 不过下面使用 ConcurrentHashMap 类型, 这样可以调用更多的方法。

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("foo", "bar");
map.put("han", "solo");
map.put("r2", "d2");
map.put("c3", "p0");
复制代码

Java 8 引入了三种并行操作:forEach, searchreduce。 每个操作都有四种形式, 分别用 key, value, entrieskey-value 来作为参数。

所有这些方法的第一个参数都是 parallelismThreshold 阀值。 该阈值表示操作并行执行时的最小收集大小。 例如, 如果传递的阈值为500,并且 map 的实际大小为499, 则操作将在单个线程上按顺序执行。 在下面的例子中,我们使用一个阈值来强制并行操作。

ForEach

方法 forEach() 能够并行地迭代 map 的键值对。 BiConsumer 类型的 lambda 表达式接受当前迭代的 keyvalue。 为了可视化并行执行,我们将当前线程名称打印到控制台。 请记住,在我的情况下,底层的 ForkJoinPool 最多使用三个线程。

map.forEach(1, (key, value) ->System.out.printf("key: %s; value: %s; thread: %s\n",key, value, Thread.currentThread().getName()));// key: r2; value: d2; thread: main
// key: foo; value: bar; thread: ForkJoinPool.commonPool-worker-1
// key: han; value: solo; thread: ForkJoinPool.commonPool-worker-2
// key: c3; value: p0; thread: main
复制代码

Search

search() 方法接受一个 BiFunction 类型的 lambda 表达式, 它能对 map 做搜索操作, 如果当前迭代不符合所需的搜索条件,则返回 null。 请记住,ConcurrentHashMap 是无序的。 搜索功能不应该取决于地图的实际处理顺序。 如果有多个匹配结果, 则结果可能是不确定的。

String result = map.search(1, (key, value) -> {System.out.println(Thread.currentThread().getName());if ("foo".equals(key)) {return value;}return null;
});
System.out.println("Result: " + result);// ForkJoinPool.commonPool-worker-2
// main
// ForkJoinPool.commonPool-worker-3
// Result: bar
复制代码

下面是对 value 的搜索

String result = map.searchValues(1, value -> {System.out.println(Thread.currentThread().getName());if (value.length() > 3) {return value;}return null;
});System.out.println("Result: " + result);// ForkJoinPool.commonPool-worker-2
// main
// main
// ForkJoinPool.commonPool-worker-1
// Result: solo
复制代码

Reduce

reduce() 方法接受两个类型为 BiFunctionlambda 表达式。 第一个函数将每个键值对转换为任何类型的单个值。 第二个函数将所有这些转换后的值组合成一个结果, 其中火忽略 null 值。

String result = map.reduce(1,(key, value) -> {System.out.println("Transform: " + Thread.currentThread().getName());return key + "=" + value;},(s1, s2) -> {System.out.println("Reduce: " + Thread.currentThread().getName());return s1 + ", " + s2;});System.out.println("Result: " + result);// Transform: ForkJoinPool.commonPool-worker-2
// Transform: main
// Transform: ForkJoinPool.commonPool-worker-3
// Reduce: ForkJoinPool.commonPool-worker-3
// Transform: main
// Reduce: main
// Reduce: main
// Result: r2=d2, c3=p0, han=solo, foo=bar
复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/278463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

this表示当前对象简单实例

直接上代码 class Message { private Channel channel ; // 保存消息发送通道 private String title ; // 消息标题 private String content ; // 消息内容 // 4、调用此构造实例化&#xff0c;此时的channel 主类ch public Message(Channel channel,String title,String cont…

twitter推文不收录_如何使用Twitter书签保存推文供以后使用

twitter推文不收录Khamosh PathakKhamosh PathakTwitter has a new Bookmarks feature that lets you privately save tweets for later. If you’ve been using the Like feature as a workaround for saving tweets, here’s why you should start bookmarking. Twitter具有一…

if的作用域问题 *输出1~6的随机数*

1 //测试if语句2 public class TestIf {3 public static void main(String[] args){4 double d Math.random();//0~1之间的小数5 int e (int)(d*5); //[0,4]6 //int f 1(int)(d*6); //[1,6] 掷色子7 System.out.println(e);8 …

为您的Blogger博客设计一个美丽的新主题

Would you like to give your Blogger blog a fresh coat of paint with a new theme? Here’s how you can use the new Template Designer to make your Blogger site stand out from the crowd and look great. 您想给Blogger博客一个新的主题吗&#xff1f; 您可以通过以…

Lab 6-4

In this lab, we’ll analyze the malware found in the file Lab06-04.exe. Questions and Short Answers What is the difference between the calls made from the main method in Labs 6-3 and 6-4? A: The function at 0x401000 is the check Internet connection method…

步入三十岁前的总结:看似经历很多得到很多,但,实际却一无所得

本文算是一篇审视自己的文章吧&#xff0c;感觉跟我类似经历的人应该很多&#xff0c;认同感应该也大一些。我是12年网络专业很普通的一所大专院校毕业&#xff0c;到现在为止工作已经超过五年。这五年里&#xff0c;做过运维工程师&#xff0c;也在小车床工作间里做了一下技工…

vue---day03

1. Vue的生命周期 - 创建和销毁的时候可以做一些我们自己的事情 - beforeCreated - created - beforeMount - mounted - beforeUpdate - updated - activated - deactivated - beforeDestroy - destroyed 1.1 知识点回顾 1.1.1 be…

U Sparkle 开发者计划招募中!

向我们投稿吧 在此之前&#xff0c;我们有收到过几篇民间高手的投稿&#xff0c;如&#xff1a; USequencer 初识&#xff08;作者&#xff1a;焱燚(七火)&#xff09; Unity游戏界面解决方案: PSD For UGUI&#xff08;作者&#xff1a;张俊钦&#xff09; UGUI 降低填充率技巧…

阶乘和 大整数

///大整数阶乘的和 #include<bits/stdc.h> using namespace std; int main() {int n;while(cin>>n){int a[2000] {1},b[2000] {0}; //存放结果的数组a。int c; //b用于存放每位存放的结果。int r0; //r用来表示进位的数。int h1,hb1; //h用来表示运算过程中 结果a…

如何添加引文标_如何在Google文档中查找和添加引文

如何添加引文标When writing papers, you need to generate a detailed and accurate list of all the sources you’ve cited in your paper. With Google Docs, you can easily find and then add citations to all of your research papers. 撰写论文时&#xff0c;您需要生…

mongo ttl索引

db.log_events.find() # 查找log_events里的所有数据db.log_events.createIndex( { "LogDT": 1 }, { expireAfterSeconds: 3600 } ) #设置log_events里的TTL过期索引清理时间为3600秒db.runComman…

Linux Centos下SQL Server 2017安装和配置

Linux Centos下SQL Server 2017安装和配置 原文:Linux Centos下SQL Server 2017安装和配置我们知道在Linux下安装服务有很多方式&#xff0c;最为简单的也就是yum安装&#xff0c;但是很多服务通过yum是无法安装的&#xff0c;如果想使用yum安装&#xff0c;需要指定yum安装仓库…

如何在Linux上使用端口敲门(以及为什么不应该这样做)

Photographee.eu/ShutterstockPhotographee.eu/ShutterstockPort knocking is a way to secure a server by closing firewall ports—even those you know will be used. Those ports are opened on demand if—and only if—the connection request provides the secret knoc…

小到年货大到产业,刘村长的扶贫模式有点厉害!

河北省阜平县平石头村的村民&#xff0c;今年春节再也不用头疼买什么年货&#xff0c;去哪买年货的问题了&#xff0c;因为他们的“村长”刘强东&#xff0c;给每户人家都送来了年货大礼包&#xff01;大礼包里不仅有牛奶、果汁、毛衣、长裤、波司登羽绒服、枕头、毛巾、炊大皇…

java - 匿名类

匿名内部类 概念&#xff1a;即内部类的简化写法 前提&#xff1a;存在一个类&#xff08;可以是具体类也可以是抽象类&#xff09;或接口 格式&#xff1a;new 类名或接口名{重写的方法} 本质&#xff1a;创建的是继承了类或实现了接口的子类匿名对 象。 匿名类总是final&…

leetcode 342. Power of Four

没想出来不用循环的。记录下。 如果是2的次方&#xff0c;必有num & (nums - 1) bool isPowerOfFour(int num) {if (num < 1) return false;if (num & (num - 1)) return false; // 排除不是2的倍数if (num & 0x55555555) return true; // 排除不是4的倍数&am…

克隆ubuntu硬盘_使用Ubuntu Live CD克隆硬盘

克隆ubuntu硬盘Whether you’re setting up multiple computers or doing a full backup, cloning hard drives is a common maintenance task. Don’t bother burning a new boot CD or paying for new software – you can do it easily with your Ubuntu Live CD. 无论是设置…

页面缓存处理的几种方法

html只要加在头部就可以了. <HEAD> <META HTTP-EQUIV"Pragma" CONTENT"no-cache"> <META HTTP-EQUIV"Cache-Control" CONTENT"no-cache"> <META HTTP-EQUIV"Expires" CONTENT"0"> </H…

Nginx的Mainline version、Stable version、Legacy version的版本区别

Nginx官网提供了三个类型的版本Mainline version&#xff1a;Mainline 是 Nginx 目前主力在做的版本&#xff0c;可以说是开发版Stable version&#xff1a;最新稳定版&#xff0c;生产环境上建议使用的版本Legacy versions&#xff1a;遗留的老版本的稳定版 nginx下载地址&…

从Boxee的Amie Street访问音乐

One of our favorite sites for discovering new music is Amie Street. Today we take a look at the Amie Street app for Boxee that allows you to access your favorite tunes from the Boxee interface. 我们最喜欢的发现新音乐的网站之一是Amie Street。 今天&#xff0…