Java SE 8 并发增强
1.原子值
java5开始,提供了一些原子操作的类,如AtomicInteger、AtomicLong等
这些类提供了诸如incrementAndGet这样的原子操作方法。
单数如果想进行复杂操作,则需要使用compareAndSet进行循环处理
do { // .. 计算 } while (!atomicLong.compareAndSet(old, new));
在java8中提供了updateAndGet和accumulateAndGet方法
atomicLong,updateAndGet(x -> Max.max(x, observed));
atomicLong.accumulateAndGet(observed, Math::max);
同时也提供了返回原始值的对应方法:getAndUpdate、getAndAccumulate
------------------------------------------------------
当大量线程访问同一个原始值时,由于乐观锁重试次数太多会导致性能下降
Java8为此提供了LongAdder和LongAccumulator解决该问题
其思想为将初始值变为多个中立元素,计算时不同线程可以对不同元素进行操作,最后再将操作结果合并。
例如:
LongAccumulator adder = new LongAccumulator (Long::sum, ); adder.accumulate(value);
此时在LongAccumulator 中包含多个中立元素a1,a2...aN.该例子下中立元素初始值都为零。当调用accumulate方法累加value时,这些变量的其中之一被更新为ai = ai op v。在这个实力中ai = ai + v;
而最后调用get方法的时候,结果为a1 op a2 op ... aN. 在上述例子中为a1+a2+...aN
------------------------------------------------------
java8中还添加了StampedLock类实现乐观读
调用tryOptimisticRead方法时会获取一个印戳,当读取值并检测印戳有效,则可以使用这个值,否则会获得一个阻塞所有写锁的读锁
例:
public class Vector {
private int size;
private Object[] elements;
private StampedLock lock = new StampedLock();
public Object get(int n) {
long stamp = lock.tryOptimisticRead();
Object[] currentElements = elements;
int currentSize = size;
if (!lock.validate(stamp)) { // Someone else had a write lock
stamp = lock.readLock(); // Get a pessimistic lock
currentElements = elements;
currentSize = size;
lock.unlockRead(stamp);
}
return n < currentSize ? currentElements[n] : null;
}
...
2.ConcurrentHashMap改进
1. 更新值
concurrentHashMap在更新数值的时候虽然是线程安全的,但是在计算更新值的时候由于不能保证线程安全,更新的值可能是错误的。
一种补救措施是使用replace
例:
do {
oldValue = map.get(word);
newValue = oldValue == null ? : oldValue + ;
} while (!map.replace(key, oldValue, newValue));
此外还可以使用利用原子对象,例如CuncurrentHashMap<String, LongAdder>
map.putIfAbsent(word, new LongAdder());
map.get(word).increment();
如果需要复杂计算,compute方法可以通过一个函数来计算新的值
map.compute(word, (k, v) -> v == null ? : v + );
xxxIfPresent和xxxIfAbsent方法分别表示已经存在值或者尚未存在值的情况下才进行操作
merge方法可以在key第一次加入时做一些特殊操作,第二个参数表示键尚未存在时的初始值
map.merge(word, 1L, (existingValue, newValue) -> existingValue + newValue); map.merge(word, 1L, Long::sum);
--------------------------------------------------------------------------
2. 批量数据操作
・search会对每个键值对领用一个函数,直到函数返回非null,search会终止并返回函数结果
・reduce会通过提供的累计函数,将所有键值对组合起来
・foreach会对所有键值对应用一个函数
每个操作都有4个版本:
• operation Keys : 对键操作
• operation Values : 对值操作
• operation: 对键和值操作
• operation Entries : 对 Map.Entry 对象操作.
以search为例,有以下几个方法:
U searchKeys(long threshold, BiFunction<? super K, ? extends U> f)
U searchValues(long threshold, BiFunction<? super V, ? extends U> f)
U search(long threshold, BiFunction<? super K, ? super V,? extends U> f)
U searchEntries(long threshold, BiFunction<Map.Entry<K, V>, ? extends U> f)
threshold为并行阀值,如果包含的元素数量超过阀值,操作会以并行方式执行,如果希望永远以单线程执行,请使用Long.MAX_VALUE
foreach和reduce方法除了上述形式外,还有另一种形式,可以提供一个转换器函数,首先会应用转换器函数,然后再将结果传递给消费者函数
map.forEach(threshold,
(k, v) -> k + " -> " + v, // Transformer
System.out::println); // Consumer
Integer maxlength = map.reduceKeys(threshold,
String::length, // Transformer
Integer::max); // Accumulator
对于int、long和double,reduce操作提供了专门的方法。以toXXX开头,需要将输入值转换为原始类型值,并指定一个默认值和累加器函数
long sum = map.reduceValuesToLong(threshold,
Long::longValue, // Transformer to primitive type
, // Default value for empty map
Long::sum); // Primitive type accumulator
-----------------------------------------------------------------------------
3. Set视图
java8没有提供concurrenHashSet类,但是可以通过concurrentHashMap类通过虚假值获得一个映射
静态方法newKeySet会返回一个Set<K>对象,它实际上是对ConcurrentHashMap<K, Boolean>对象的封装。
Set<String> words = ConcurrentHashMap.<String>newKeySet();
如果你已经有一个映射,keySet方法会返回所有键的Set,但是你不能向这个set中添加元素,因为无法向map添加相应的值
于是,一个接收默认值的keySet方法可以解决上述问题,通过这个默认值向set中添加元素
Set<String> words = map.keySet(1L);
words.add("Java");
key=java, value = 1L
3.并行数组操作
Arrays提供许多并行化操作
parallelSort可以进行并行排序,并且可以指定范围
String contents = new String(Files.readAllBytes(
Paths.get("alice.txt")), StandardCharsets.UTF_8); // Read file into string
String[] words = contents.split("[\\P{L}]+"); // Split along nonletters
Arrays.parallelSort(words);
values.parallelSort(values.length / , values.length); // 对上半部排序
parallelSetAll方法会根据提供的计算函数对参数values的每一个值进行计算并更新
Arrays.parallelSetAll(values, i -> i % );
// Fills values with 0 1 2 3 4 5 6 7 8 9 0 1 2 . . .
parallelPrefix将数组中每个元素替换为指定关联操作前缀的积累
假设array [1, 2, 3, 4, ...],执行完Arrays.parallelPrefix(values, (x, y) -> x * y)之后,array的结果为
[1, 1 × 2, 1 × 2 × 3, 1 × 2 × 3 × 4, ...]
4.可完成的Future
在过去,Future获取结果的方法为get,并且调用后会一直阻塞等待get返回结果
CompletableFuture<T>提供了“当结果可用时,再按照提供的方式处理”的功能
CompletableFuture<String> contents = readPage(url);
CompletableFuture<List<String>> links = contents.thenApply(Parser::getLinks);
thenApply方法不会被阻塞,它会返回另一个Future对象,当第一个Future对象完成时,它的结果会发给getLinks方法
Future流水线类似Steam流水线,经过一个或多个转换过程,最后由一个终止操作结束。
如下代码可以启动一个流水线
CompletableFuture<String> contents
= CompletableFuture.<strong>supplyAsync</strong>(() -> blockingReadPage(url));
另外还有一个runAsync方法,接收Runnable参数,返回CompletableFuture<void>
接下来可以调用thenApply或者thenApplyAsync方法,在同一个线程或者另一个线程中运行另一个操作。
最终这些步骤执行完毕,需要将结果保存在某个地方,需要一个终止操作,例如:
CompletableFuture<Void> links
= CompletableFuture.supplyAsync(() -> blockingReadPage(url))
.thenApply(Parser::getLinks)
.thenAccept(System.out::println);
thenAccept方法接收一个Consumer接口(返回类型为void),理想情况下不需要调用Future的get方法
以下是一些常用方法:
thenCompose方法做的事就是,假设同时有两个调用链,T->CompletableFuture<U>和U->CompletableFuture<V>在连续调用的情况下,合并为T->CompletableFuture<V>
类似的常用方法如下:
Java SE 8 并发增强的更多相关文章
- Java SE之For增强与Iterator遍历器提取数据(附Map.Entry)
增强for循环: 1.操作数组 2.操作List集合 3.操作Map集合 1.map.values()法 2.map.keySet()法 [传统方法] 3.Map.Entry法 ...
- Java SE之快速失败(Fast-Fail)与快速安全(Fast-Safe)的区别[集合与多线程/增强For](彻底详解)
声明 特点:基于JDK源码进行分析. 研究费时费力,如需转载或摘要,请显著处注明出处,以尊重劳动研究成果:博客园 - https://www.cnblogs.com/johnnyzen/p/10547 ...
- Java SE 6 新特性: HTTP 增强--转
概述 Java 语言从诞生的那天起,就非常注重网络编程方面的应用.随着互联网应用的飞速发展,Java 的基础类库也不断地对网络相关的 API 进行加强和扩展.在 Java SE 6 当中,围绕着 HT ...
- Java SE 5.0 - SE 8 的功能增强
Table of Contents 前言 Java 5.0 Generics Enhanced for Loop Autoboxing Typesafe Enums Varargs Static Im ...
- Java SE 枚举,注解,增强for循环
Java SE 进阶 1.Enum 枚举对象名通常使用全部大写,常量的命名规范 构造器私有化 本类内部创建一组对象 对外暴露对象(通过为对象添加 public final static 修饰符) 可以 ...
- Java SE教程
第0讲 开山篇 读前介绍:本文中如下文本格式是超链接,可以点击跳转 >>超链接<< 我的学习目标:基础要坚如磐石 代码要十份规范 笔记要认真详实 一.java内容介绍 ...
- Java SE 9 新增特性
Java SE 9 新增特性 作者:Grey 原文地址: Java SE 9 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...
- Java SE 10 新增特性
Java SE 10 新增特性 作者:Grey 原文地址:Java SE 10 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 19 新增特性
Java SE 19 新增特性 作者:Grey 原文地址: 博客园:Java SE 19 新增特性 CSDN:Java SE 19 新增特性 源码 源仓库: Github:java_new_featu ...
随机推荐
- maven 远程仓库的配置
setting.xml <profile> <id>development</id> <repositories> <repositor ...
- poj1703(种类并查集)
题意:有两个犯罪集团,现在有两种操作,D [a] [b]表示a和b是属于不同犯罪集团的,A [a] [b] 是询问你a和b的关系,如果ab属于同一个犯罪集团,输出In the same gang. ...
- java基础篇---XML解析(二)
XML解析之最流行的方式:DOM4J dom4j是使用java语言编写的,用于读,写,操作XML的一套组件 dom4j是一个开源的java组件,可从http://sourceforge.net/pro ...
- Python的自增运算符
今天在写一个合并两个有血list的时候,使用了while循环,不自觉的使用了i++,自测的时候发现有语法错误,还检查了好几遍,觉得应该没啥错误啊,后来google了一把,恍然大悟,原来Python早就 ...
- 新浪微博 oauth2.0 redirect_uri_mismatch
新浪微博开放平台出来很久了,现在才开始研究,貌似有点晚了.... 第一次折腾,总是出现这样那样的问题,即使照着别人成功的例子也是一样,这不,开始运行的时候,运行下面的例子,总是报error:redir ...
- Beans
PHP之所以被人称为"世界上最好的语言",很大程度上是因为学会语法后就可以直接运用其开发Web应用了吧,而Java基本上不可能.在Java的语言特性的背后,还拖着由Sun公司和社区 ...
- PostgreSQL存储过程<转>
原创文章,转载请务必将下面这段话置于文章开头处(保留超链接).本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/2015/12/27/SQL4_存储过程_St ...
- 获取linux内核的配置项(包含模块module)_转
转自:提取已有的内核配置文件 由于有时候所做的内核配置文件需要移植到其他的内核源码中,此时又忘了保存,这时以下方法就可以满足你了. 1.首先这两个配置的位于(init/Kconfig): 2. 如果要 ...
- .Net中的内存分配问题
最近在测试的时候,要求测试内存不足的情况.我不想去开很多的程序来占用内存,那样太麻烦了,也不太精确.于是就写一个小程序来占用内存,想法很简单,就是声明一个Byte数组在占用内存,没想到这么简单的想法却 ...
- 理解ThreadPoolExecutor源代码(二)execute函数的巧妙设计和阅读心得
ThreadPoolExecutor.execute()源代码提供了大量凝视来解释该方法的设计考虑.以下的源代码来自jdk1.6.0_37 public void execute(Runnable c ...