`
m635674608
  • 浏览: 4946994 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

JDK8对并发的新支持

    博客分类:
  • java
 
阅读更多

1. LongAdder

和AtomicLong类似的使用方式,但是性能比AtomicLong更好。

LongAdder与AtomicLong都是使用了原子操作来提高性能。但是LongAdder在AtomicLong的基础上进行了热点分离,热点分离类似于有锁操作中的减小锁粒度,将一个锁分离成若干个锁来提高性能。在无锁中,也可以用类似的方式来增加CAS的成功率,从而提高性能。

LongAdder原理图:

AtomicLong的实现方式是内部有个value 变量,当多线程并发自增,自减时,均通过CAS 指令从机器指令级别操作保证并发的原子性。唯一会制约AtomicLong高效的原因是高并发,高并发意味着CAS的失败几率更高, 重试次数更多,越多线程重试,CAS失败几率又越高,变成恶性循环,AtomicLong效率降低。

而LongAdder将把一个value拆分成若干cell,把所有cell加起来,就是value。所以对LongAdder进行加减操作,只需要对不同的cell来操作,不同的线程对不同的cell进行CAS操作,CAS的成功率当然高了(试想一下3+2+1=6,一个线程3+1,另一个线程2+1,最后是8,LongAdder没有乘法除法的API)。

可是在并发数不是很高的情况,拆分成若干的cell,还需要维护cell和求和,效率不如AtomicLong的实现。LongAdder用了巧妙的办法来解决了这个问题。

初始情况,LongAdder与AtomicLong是相同的,只有在CAS失败时,才会将value拆分成cell,每失败一次,都会增加cell的数量,这样在低并发时,同样高效,在高并发时,这种“自适应”的处理方式,达到一定cell数量后,CAS将不会失败,效率大大提高。

LongAdder是一种以空间换时间的策略。

2. CompletableFuture

实现CompletionStage接口(40余个方法),大多数方法多数应用在函数式编程中。并且支持流式调用

CompletableFuture是Java 8中对Future的增强版

简单实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.concurrent.CompletableFuture;
 
public class AskThread implements Runnable {
    CompletableFuture<Integer> re = null;
 
    public AskThread(CompletableFuture<Integer> re) {
        this.re = re;
    }
 
    @Override
    public void run() {
        int myRe = 0;
        try {
            myRe = re.get() * re.get();
        } catch (Exception e) {
        }
        System.out.println(myRe);
    }
 
    public static void main(String[] args) throws InterruptedException {
        final CompletableFuture<Integer> future = new CompletableFuture<Integer>();
        new Thread(new AskThread(future)).start();
        // 模拟长时间的计算过程
        Thread.sleep(1000);
        // 告知完成结果
        future.complete(60);
    }
}

Future最令人诟病的就是要等待,要自己去检查任务是否完成了,在Future中,任务完成的时间是不可控的。而CompletableFuture的最大改进在于,任务完成的时间也开放了出来。

1
future.complete(60);

用来设置完成时间。

CompletableFuture的异步执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Integer calc(Integer para) {
        try {
            // 模拟一个长时间的执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        return para * para;
    }
 
    public static void main(String[] args) throws InterruptedException,
            ExecutionException {
        final CompletableFuture<Integer> future = CompletableFuture
                .supplyAsync(() -> calc(50));
        System.out.println(future.get());
    }

CompletableFuture的流式调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static Integer calc(Integer para) {
        try {
            // 模拟一个长时间的执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        return para * para;
    }
 
    public static void main(String[] args) throws InterruptedException,
            ExecutionException {
        CompletableFuture<Void> fu = CompletableFuture
                .supplyAsync(() -> calc(50))
                .thenApply((i) -> Integer.toString(i))
                .thenApply((str) -> "\"" + str + "\"")
                .thenAccept(System.out::println);
        fu.get();
    }

组合多个CompletableFuture:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Integer calc(Integer para) {
        return para / 2;
    }
 
    public static void main(String[] args) throws InterruptedException,
            ExecutionException {
        CompletableFuture<Void> fu = CompletableFuture
                .supplyAsync(() -> calc(50))
                .thenCompose(
                        (i) -> CompletableFuture.supplyAsync(() -> calc(i)))
                .thenApply((str) -> "\"" + str + "\"")
                .thenAccept(System.out::println);
        fu.get();
    }

这几个例子更多是侧重Java8的一些新特性,这里就简单举下例子来说明特性,就不深究了。

CompletableFuture跟性能上关系不大,更多的是为了支持函数式编程,在功能上的增强。当然开放了完成时间的设置是一大亮点。

3. StampedLock

在上一篇中刚刚提到了锁分离,而锁分离的重要的实现就是ReadWriteLock。而StampedLock则是ReadWriteLock的一个改进。StampedLock与ReadWriteLock的区别在于,StampedLock认为读不应阻塞写,StampedLock认为当读写互斥的时候,读应该是重读,而不是不让写线程写。这样的设计解决了读多写少时,使用ReadWriteLock会产生写线程饥饿现象。

所以StampedLock是一种偏向于写线程的改进。

StampedLock示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.concurrent.locks.StampedLock;
 
public class Point {
    private double x, y;
    private final StampedLock sl = new StampedLock();
 
    void move(double deltaX, double deltaY) { // an exclusively locked method
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            sl.unlockWrite(stamp);
        }
    }
 
    double distanceFromOrigin() { // A read-only method
        long stamp = sl.tryOptimisticRead();
        double currentX = x, currentY = y;
        if (!sl.validate(stamp)) {
            stamp = sl.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                sl.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}

上述代码模拟了写线程和读线程, StampedLock根据stamp来查看是否互斥,写一次stamp变增加某个值

1
tryOptimisticRead()

就是刚刚所说的读写不互斥的情况。

每次读线程要读时,会先判断

1
if (!sl.validate(stamp))

validate中会先查看是否有写线程在写,然后再判断输入的值和当前的 stamp是否相同,即判断是否读线程将读到最新的数据。如果有写线程在写,或者 stamp数值不同,则返回失败。

如果判断失败,当然可以重复的尝试去读,在示例代码中,并没有让其重复尝试读,而采用的是将乐观锁退化成普通的读锁去读,这种情况就是一种悲观的读法。

1
stamp = sl.readLock();

StampedLock的实现思想:

CLH自旋锁:当锁申请失败时,不会立即将读线程挂起,在锁当中会维护一个等待线程队列,所有申请锁,但是没有成功的线程都记录在这个队列中。每一个节点(一个节点代表一个线程),保存一个标记位(locked),用于判断当前线程是否已经释放锁。当一个线程试图获得锁时,取得当前等待队列的尾部节点作为其前序节点。并使用类似如下代码判断前序节点是否已经成功释放锁

1
2
while (pred.locked) {  
}

这个循环就是不断等前面那个结点释放锁,这样的自旋使得当前线程不会被操作系统挂起,从而提高了性能。

当然不会进行无休止的自旋,会在若干次自旋后挂起线程。

本系列:

http://www.importnew.com/21358.html

分享到:
评论

相关推荐

    Java 高并发十: JDK8对并发的新支持详解

    本文主要介绍Java 高并发JDK8的支持,这里整理了详细的资料及1. LongAdder 2. CompletableFuture 3. StampedLock的介绍,有兴趣的小伙伴可以参考下

    JDK5中的并发(多线程)

    JVM 已经进行了改进,允许类利用硬件级别支持并发,并且提供了一组丰富的新并发构造块,使开发并发应用程序更加容易。 &lt;br&gt;本教程将介绍 JDK 5.0 提供的用于并发的新实用程序类,并通过与现有并发原语...

    最新【Java19】分享Linux和Windows版【jdk19】

    该版本有七项功能,包括结构化并发、记录模式、外部函数和内存 API 的预览,以及对开源 Linux/RISC-V 指令集体系结构 (ISA) 的支持。除 Linux/RISC-V 功能外,所有功能都处于预览阶段或孵化器阶段。 Java 开发工具...

    jdk-8u181-windows64位安装版.zip

    JDK8新特性:  1.Lambda表达式  2.新的日期API  3.引入Optional  4.使用Base64  5.接口的默认方法和静态方法  6.新增方法引用格式  7.新增Stream类  8.注解相关的改变  9.支持并行(parallel)数组  10.对...

    基于JDK源码解析Java领域中的并发锁之设计与实现.pdf

    在Java领域中, 尤其是在并发编程领域,对于多线程并发执行一直有两大核心问题:同步和互斥。...为了弥补这些问题,从JDK层面对其“重复造轮子”,在JDK内部对其重新设计和定义,甚至实现了新的特性。

    Java高并发实战_java高并发_高并发_

    第2章介绍了 Java 并行程序开发的基础, 包括 Java 中 Thread 的基本使用方法等第3章介绍了 JDK 内部对并行程序开发的支持, 主要介绍 JUC (Java.util.concurrent) 中些工具的使用方法、 各自特点及它们的内部实现...

    JDK+8.0+EN+API.chm.zip

    JDK8新特性:  1.Lambda表达式  2.新的日期API  3.引入Optional  4.使用Base64  5.接口的默认方法和静态方法  6.新增方法引用格式  7.新增Stream类  8.注解相关的改变  9.支持并行(parallel)...

    JDK21虚拟线程详解.pptx.pptx

    JDK21的虚拟线程,不需要操作系统的原生支持,可以在没有物理机的情况下运行,极大地降低了线程的创建和管理开销。 普通线程的实现方式 普通线程需要操作系统的原生支持,它的创建和管理都需要消耗一定的系统资源,...

    实战Java高并发程序设计.mobi

    实战Java高并发程序设计主要介绍基于Java的并行程序设计基础、思路、方法和...第四,介绍并行的基本设计模式及Java 8对并行程序的支持和改进。第五,介绍高并发框架Akka的使用方法。最后,详细介绍并行程序的调试方法。

    Java并发jdk源码剖析-jmc7:https://openjdk.java.net/projects/jmc/

    支持对 Java 飞行记录进行无头分析 应用特点 用于托管各种有用 Java 工具的应用程序支持框架 一种用于可视化 Java 飞行记录内容以及内容自动分析结果的工具 JMX 控制台 用于堆废物分析的工具 核心 API 示例 从命令行...

    Java并发编程原理与实战

    Spring对并发的支持:Spring的异步任务.mp4 使用jdk8提供的lambda进行并行计算.mp4 了解多线程所带来的安全风险.mp4 从线程的优先级看饥饿问题.mp4 从Java字节码的角度看线程安全性问题.mp4 synchronized保证线程...

    【2018最新最详细】并发多线程教程

    14.并发容器之ConcurrentHashMap(JDK 1.8版本) 15.并发容器之ConcurrentLinkedQueue 16.并发容器之CopyOnWriteArrayList 17.并发容器之ThreadLocal 18.一篇文章,从源码深入详解ThreadLocal内存泄漏问题 19.并发容器...

    实战java高并发程序设计.pdf

    第四,介绍并行的基本设计模式及Java 8对并行程序的支持和改进。第五,介绍高并发框架Akka的使用方法。最后,详细介绍并行程序的调试方法。 《实战Java高并发程序设计》内容丰富,实例典型,实用性强,适合有一定...

    jdk1.8-windows以及Linux

    Java Development Kit (JDK) 1.8是一个Java平台的开发环境。它提供了Java编译器、Java运行...此外,JDK 1.8还包含了JavaFX 8,一个新的Java图形用户界面工具包,以及新的工具和API,用于Java并发编程和日期/时间处理。

    实战Java高并发程序设计 中文pdf版下载

    第四,介绍并行的基本设计模式及Java8对并行程序的支持和改进。第五,介绍高并发框架Akka的使用方法。最后,详细介绍并行程序的调试方法。 实战Java高并发程序设计内容丰富,实例典型,实用性强,适合有一定Java基础...

    实战Java高并发程序设计 .pdf 下载

    第四,介绍并行的基本设计模式及Java 8对并行程序的支持和改进。第五,介绍高并发框架Akka的使用方法。最后,详细介绍并行程序的调试方法。 《实战Java高并发程序设计》内容丰富,实例典型,实用性强,适合有一定...

    实战Java高并发程序设计.zip

    第四,介绍并行的基本设计模式及Java 8对并行程序的支持和改进。第五,介绍高并发框架Akka的使用方法。最后,详细介绍并行程序的调试方法。 《实战Java高并发程序设计》内容丰富,实例典型,实用性强,适合有一定...

    Java并发程序设计教程

    第四,介绍并行的基本设计模式及Java 8对并行程序的支持和改进。第五,介绍高并发框架Akka的使用方法。最后,详细介绍并行程序的调试方法。 《实战Java高并发程序设计》内容丰富,实例典型,实用性强,适合有一定...

    实战Java高并发程序设计.pdf 带书签+源码 (密码:ouwen)高清

    带部分书签,内容清晰。 实战Java高并发程序设计主要介绍基于Java的并行...第四,介绍并行的基本设计模式及Java 8对并行程序的支持和改进。第五,介绍高并发框架Akka的使用方法。最后,详细介绍并行程序的调试方法。

    jdk11jar架包文件下载

    高性能并发编程:JDK 11引入了一批新的并发处理库和工具,如VarHandles和ForkJoin框架的改进,使得多线程编程更加高效和简洁。 安全性提升:JDK 11在安全性方面进行了大量改进,包括对密钥管理、证书验证、安全协议...

Global site tag (gtag.js) - Google Analytics