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 并发增强的更多相关文章

  1. Java SE之For增强与Iterator遍历器提取数据(附Map.Entry)

    增强for循环: 1.操作数组 2.操作List集合 3.操作Map集合    1.map.values()法    2.map.keySet()法  [传统方法]    3.Map.Entry法   ...

  2. Java SE之快速失败(Fast-Fail)与快速安全(Fast-Safe)的区别[集合与多线程/增强For](彻底详解)

    声明 特点:基于JDK源码进行分析. 研究费时费力,如需转载或摘要,请显著处注明出处,以尊重劳动研究成果:博客园 - https://www.cnblogs.com/johnnyzen/p/10547 ...

  3. Java SE 6 新特性: HTTP 增强--转

    概述 Java 语言从诞生的那天起,就非常注重网络编程方面的应用.随着互联网应用的飞速发展,Java 的基础类库也不断地对网络相关的 API 进行加强和扩展.在 Java SE 6 当中,围绕着 HT ...

  4. Java SE 5.0 - SE 8 的功能增强

    Table of Contents 前言 Java 5.0 Generics Enhanced for Loop Autoboxing Typesafe Enums Varargs Static Im ...

  5. Java SE 枚举,注解,增强for循环

    Java SE 进阶 1.Enum 枚举对象名通常使用全部大写,常量的命名规范 构造器私有化 本类内部创建一组对象 对外暴露对象(通过为对象添加 public final static 修饰符) 可以 ...

  6. Java SE教程

    第0讲 开山篇 读前介绍:本文中如下文本格式是超链接,可以点击跳转 >>超链接<< 我的学习目标:基础要坚如磐石   代码要十份规范   笔记要认真详实 一.java内容介绍 ...

  7. Java SE 9 新增特性

    Java SE 9 新增特性 作者:Grey 原文地址: Java SE 9 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...

  8. Java SE 10 新增特性

    Java SE 10 新增特性 作者:Grey 原文地址:Java SE 10 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  9. Java SE 19 新增特性

    Java SE 19 新增特性 作者:Grey 原文地址: 博客园:Java SE 19 新增特性 CSDN:Java SE 19 新增特性 源码 源仓库: Github:java_new_featu ...

随机推荐

  1. QQ通信原理及QQ是怎么穿透内网进行通信的?

    http://blog.csdn.net/frank_good/article/details/51160027 ******************************************* ...

  2. Liunx下Intel无线网卡驱动安装

    原文: https://blog.csdn.net/u014157776/article/details/78272611 首先查看网卡型号,指令: lspci | grep -i net 如果是In ...

  3. JAVA-数据库之更新记录

    相关资料:<21天学通Java Web开发> 更新记录 StatementUpdate.jsp <%@ page language="java" contentT ...

  4. JAVA-JSP内置对象之application对象获得服务器版本

    相关资料:<21天学通Java Web开发> application对象获得服务器版本1.通过application对象的getMajorVersion()方法和getMinorVersi ...

  5. 老项目Xcode5.1编译器错误

    老项目编译器错误html, body {overflow-x: initial !important;}html { font-size: 14px; } body { margin: 0px; pa ...

  6. 将已有jar添加至本地maven仓库

    官网链接:http://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html 比如有 commons-dbutils-1.6.jar ...

  7. jmx学习

    原文地址:https://www.cnblogs.com/dongguacai/p/5900507.html 一.JMX的定义 JMX(Java Management Extensions)是一个为应 ...

  8. Extjs4.x Ext.tree.Panel 遍历当前节点下的所有子节点

    Ext.define('WMS.controller.Org', { extend: 'Ext.app.Controller', stores: ['OrgUser', 'OrgTree'], mod ...

  9. Redis Java连接操作

    安装 要在Java程序中使用使用操作Redis,需要确保有Redis的Java驱动程序和Java设置在机器上.可以检查看Java教程-学习如何在机器上安装Java.现在,让我们来看看如何设置Redis ...

  10. 加密安装Kali Linux条件

    加密安装Kali Linux条件安装Kali Linux到你的电脑过程很简单.首先你需要兼容的电脑硬件.最低硬件要求如下,更好的硬件性能会更好.i386镜象默认使用PAE内核,所以你能在大于4GB内存 ...