`
xinklabi
  • 浏览: 1560691 次
  • 性别: Icon_minigender_1
  • 来自: 吉林
文章分类
社区版块
存档分类
最新评论

JDK 7 中的 Fork/Join 模式(并发处理)

 
阅读更多

转自:http://www.ibm.com/developerworks/cn/java/j-lo-forkjoin/

介绍

随着多核芯片逐渐成为主流,大多数软件开发人员不可避免地需要了解并行编程的知识。而同时,

主流程序语言正在将越来越多的并行特性合并到标准库或者语言本身之中。我们可以看到,JDK

在这方面同样走在潮流的前方。在 JDK 标准版 5 中,由 Doug Lea 提供的并行框架成为了标准库

的一部分(JSR-166)。随后,在 JDK 6 中,一些新的并行特性,例如并行 collection 框架,合并

到了标准库中(JSR-166x)。直到今天,尽管 Java SE 7 还没有正式发布,一些并行相关的新特

性已经出现在 JSR-166y 中:

  1. Fork/Join 模式;
  2. TransferQueue,它继承自 BlockingQueue 并能在队列满时阻塞“生产者”;
  3. ArrayTasks/ListTasks,用于并行执行某些数组/列表相关任务的类;
  4. IntTasks/LongTasks/DoubleTasks,用于并行处理数字类型数组的工具类,提供了排序、查找、
  5. 求和、求最小值、求最大值等功能;

其中,对 Fork/Join 模式的支持可能是对开发并行软件来说最通用的新特性。在 JSR-166y 中,

Doug Lea 实现 ArrayTasks/ListTasks/IntTasks/LongTasks/DoubleTasks 时就大量的用到了 Fork/Join

模式。读者还需要注意一点,因为 JDK 7 还没有正式发布,因此本文涉及到的功能和发布版本有可

能不一样。

Fork/Join 模式有自己的适用范围。如果一个应用能被分解成多个子任务,并且组合多个子任务的结

果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决。图 1 给出了一个

Fork/Join 模式的示意图,位于图上部的 Task 依赖于位于其下的 Task 的执行,只有当所有的子任

务都完成之后,调用者才能获得 Task 0 的返回结果。

图 1. Fork/Join 模式示意图
图 1. Fork/Join 模式示意图

可以说,Fork/Join 模式能够解决很多种类的并行问题。通过使用 Doug Lea 提供的 Fork/Join 框架,

软件开发人员只需要关注任务的划分和中间结果的组合就能充分利用并行平台的优良性能。其他

和并行相关的诸多难于处理的问题,例如负载

平衡、同步等,都可以由框架采用统一的方式解决。这样,我们就能够轻松地获得并行的好处而

避免了并行编程的困难且容易出错的缺点。

 

使用 Fork/Join 模式

在开始尝试 Fork/Join 模式之前,我们需要从 Doug Lea 主持的 Concurrency JSR-166 Interest Site

上下载 JSR-166y 的源代码,并且我们还需要安装最新版本的 JDK 6(下载网址请参阅 参考资源

。Fork/Join 模式的使用方式非常直观。首先,我们需要编写一个 ForkJoinTask 来完成子任务的分

割、中间结果的合并等工作。随后,我们将这个 ForkJoinTask 交给 ForkJoinPool 来完成应用的执

行。

通常我们并不直接继承 ForkJoinTask,它包含了太多的抽象方法。针对特定的问题,我们可以选择 ForkJoinTask 的不同子类来完成任务。RecursiveAction 是 ForkJoinTask 的一个子类,它代表了一类最简单的 ForkJoinTask:不需要返回

值,当子任务都执行完毕之后,不需要进行中间结果的组合。如果我们从 RecursiveAction 开始继

承,那么我们只需要重载 protected void compute() 方法。下面,我们来看看怎么为快速

排序算法建立一个 ForkJoinTask 的子类:

清单 1. ForkJoinTask 的子类
class SortTask extends RecursiveAction {
    final long[] array;
    final int lo;
    final int hi;
    private int THRESHOLD = 30;

    public SortTask(long[] array) {
        this.array = array;
        this.lo = 0;
        this.hi = array.length - 1;
    }

    public SortTask(long[] array, int lo, int hi) {
        this.array = array;
        this.lo = lo;
        this.hi = hi;
    }

    protected void compute() {
        if (hi - lo < THRESHOLD)
            sequentiallySort(array, lo, hi);
        else {
            int pivot = partition(array, lo, hi);
            coInvoke(new SortTask(array, lo, pivot - 1), new SortTask(array,
                pivot + 1, hi));
        }
    }

    private int partition(long[] array, int lo, int hi) {
        long x = array[hi];
        int i = lo - 1;
        for (int j = lo; j < hi; j++) {
            if (array[j] <= x) {
                i++;
                swap(array, i, j);
            }
        }
        swap(array, i + 1, hi);
        return i + 1;
    }

    private void swap(long[] array, int i, int j) {
        if (i != j) {
            long temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }

    private void sequentiallySort(long[] array, int lo, int hi) {
        Arrays.sort(array, lo, hi + 1);
    }
}

在 清单 1 中,SortTask 首先通过 partition() 方法将数组分成两个部分。随后,两个子任务将

被生成并分别排序数组的两个部分。当子任务足够小时,再将其分割为更小的任务反而引起性能的

降低。因此,这里我们使用一个 THRESHOLD,限定在子任务规模较小时,使用直接排序,而不是

再将其分割成为更小的任务。其中,我们用到了 RecursiveAction 提供的方法 coInvoke()。它表

示:启动所有的任务,并在所有任务都正常结束后返回。如果其中一个任务出现异常,则其它所有

的任务都取消。coInvoke() 的参数还可以是任务的数组。

现在剩下的工作就是将 SortTask 提交到 ForkJoinPool 了。ForkJoinPool() 默认建立具有与

CPU 可使用线程数相等线程个数的线程池。我们在一个 JUnit 的 test 方法中将 SortTask 提交给

一个新建的 ForkJoinPool:

清单 2. 新建的 ForkJoinPool
@Test
public void testSort() throws Exception {
    ForkJoinTask sort = new SortTask(array);
    ForkJoinPool fjpool = new ForkJoinPool();
    fjpool.submit(sort);
    fjpool.shutdown();

    fjpool.awaitTermination(30, TimeUnit.SECONDS);

    assertTrue(checkSorted(array));
}

在上面的代码中,我们用到了 ForkJoinPool 提供的如下函数:

  1. submit():将 ForkJoinTask 类的对象提交给 ForkJoinPool,ForkJoinPool 将立刻开始执行
  2. ForkJoinTask。
  3. shutdown():执行此方法之后,ForkJoinPool 不再接受新的任务,但是已经提交的任务可以
  4. 继续执行。如果希望立刻停止所有的任务,可以尝试 shutdownNow() 方法。
  5. awaitTermination():阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束。

并行快速排序的完整代码如下所示:

清单 3. 并行快速排序的完整代码
package tests;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import jsr166y.forkjoin.ForkJoinPool;
import jsr166y.forkjoin.ForkJoinTask;
import jsr166y.forkjoin.RecursiveAction;

import org.junit.Before;
import org.junit.Test;

class SortTask extends RecursiveAction {
    final long[] array;
    final int lo;
    final int hi;
    private int THRESHOLD = 0; //For demo only

    public SortTask(long[] array) {
        this.array = array;
        this.lo = 0;
        this.hi = array.length - 1;
    }

    public SortTask(long[] array, int lo, int hi) {
        this.array = array;
        this.lo = lo;
        this.hi = hi;
    }

    protected void compute() {
        if (hi - lo < THRESHOLD)
            sequentiallySort(array, lo, hi);
        else {
            int pivot = partition(array, lo, hi);
            System.out.println("\npivot = " + pivot + ", low = " + lo + ", high = " + hi);
			System.out.println("array" + Arrays.toString(array));
            coInvoke(new SortTask(array, lo, pivot - 1), new SortTask(array,
                    pivot + 1, hi));
        }
    }

    private int partition(long[] array, int lo, int hi) {
        long x = array[hi];
        int i = lo - 1;
        for (int j = lo; j < hi; j++) {
            if (array[j] <= x) {
                i++;
                swap(array, i, j);
            }
        }
        swap(array, i + 1, hi);
        return i + 1;
    }

    private void swap(long[] array, int i, int j) {
        if (i != j) {
            long temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }

    private void sequentiallySort(long[] array, int lo, int hi) {
        Arrays.sort(array, lo, hi + 1);
    }
}

public class TestForkJoinSimple {
    private static final int NARRAY = 16; //For demo only
    long[] array = new long[NARRAY];
    Random rand = new Random();

    @Before
    public void setUp() {
        for (int i = 0; i < array.length; i++) {
            array[i] = rand.nextLong()%100; //For demo only
        }
        System.out.println("Initial Array: " + Arrays.toString(array));
    }

    @Test
    public void testSort() throws Exception {
        ForkJoinTask sort = new SortTask(array);
        ForkJoinPool fjpool = new ForkJoinPool();
        fjpool.submit(sort);
        fjpool.shutdown();

        fjpool.awaitTermination(30, TimeUnit.SECONDS);

        assertTrue(checkSorted(array));
    }

    boolean checkSorted(long[] a) {
        for (int i = 0; i < a.length - 1; i++) {
            if (a[i] > (a[i + 1])) {
                return false;
            }
        }
        return true;
    }
}

运行以上代码,我们可以得到以下结果:

Initial Array: [46, -12, 74, -67, 76, -13, -91, -96]

pivot = 0, low = 0, high = 7
array[-96, -12, 74, -67, 76, -13, -91, 46]

pivot = 5, low = 1, high = 7
array[-96, -12, -67, -13, -91, 46, 76, 74]

pivot = 1, low = 1, high = 4
array[-96, -91, -67, -13, -12, 46, 74, 76]

pivot = 4, low = 2, high = 4
array[-96, -91, -67, -13, -12, 46, 74, 76]

pivot = 3, low = 2, high = 3
array[-96, -91, -67, -13, -12, 46, 74, 76]

pivot = 2, low = 2, high = 2
array[-96, -91, -67, -13, -12, 46, 74, 76]

pivot = 6, low = 6, high = 7
array[-96, -91, -67, -13, -12, 46, 74, 76]

pivot = 7, low = 7, high = 7
array[-96, -91, -67, -13, -12, 46, 74, 76]
 

Fork/Join 模式高级特性

使用 RecursiveTask

除了 RecursiveAction,Fork/Join 框架还提供了其他 ForkJoinTask 子类:带有返回值的

RecursiveTask,使用 finish() 方法显式中止的 AsyncAction 和 LinkedAsyncAction,以及可使

用 TaskBarrier 为每个任务设置不同中止条件的 CyclicAction。

从 RecursiveTask 继承的子类同样需要重载 protected void compute() 方法。与

RecursiveAction 稍有不同的是,它可使用泛型指定一个返回值的类型。下面,我们来看看如何使

用 RecursiveTask 的子类。

清单 4. RecursiveTask 的子类
class Fibonacci extends RecursiveTask<Integer> {
    final int n;

    Fibonacci(int n) {
        this.n = n;
    }

    private int compute(int small) {
        final int[] results = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
        return results[small];
    }

    public Integer compute() {
        if (n <= 10) {
            return compute(n);
        }
        Fibonacci f1 = new Fibonacci(n - 1);
        Fibonacci f2 = new Fibonacci(n - 2);
        f1.fork();
        f2.fork();
        return f1.join() + f2.join();
    }
}

在 清单 4 中, Fibonacci 的返回值为 Integer 类型。其 compute() 函数首先建立两个子任务,启

动子任务执行,阻塞以等待子任务的结果返回,相加后得到最终结果。同样,当子任务足够小时,

通过查表得到其结果,以减小因过多地分割任务引起的性能降低。其中,我们用到了

RecursiveTask 提供的方法 fork() 和 join()。它们分别表示:子任务的异步执行和阻塞等待结

果完成。

现在剩下的工作就是将 Fibonacci 提交到 ForkJoinPool 了,我们在一个 JUnit 的 test 方法中作了

如下处理:

清单 5. 将 Fibonacci 提交到 ForkJoinPool
@Test
public void testFibonacci() throws InterruptedException, ExecutionException {
    ForkJoinTask<Integer> fjt = new Fibonacci(45);
    ForkJoinPool fjpool = new ForkJoinPool();
    Future<Integer> result = fjpool.submit(fjt);

    // do something
    System.out.println(result.get());
}

使用 CyclicAction 来处理循环任务

CyclicAction 的用法稍微复杂一些。如果一个复杂任务需要几个线程协作完成,并且线程之间需要

在某个点等待所有其他线程到达,那么我们就能方便的用 CyclicAction 和 TaskBarrier 来完成。

图 2 描述了使用 CyclicAction 和 TaskBarrier 的一个典型场景。

图 2. 使用 CyclicAction 和 TaskBarrier 执行多线程任务
图 2. 使用 CyclicAction 和 TaskBarrier 执行多线程任务

继承自 CyclicAction 的子类需要 TaskBarrier 为每个任务设置不同的中止条件。从 CyclicAction 继承

的子类需要重载 protected void compute() 方法,定义在 barrier 的每个步骤需要执行的

动作。compute() 方法将被反复执行直到 barrier 的 isTerminated() 方法返回 True

TaskBarrier 的行为类似于 CyclicBarrier。下面,我们来看看如何使用 CyclicAction 的子类。

清单 6. 使用 CyclicAction 的子类
class ConcurrentPrint extends RecursiveAction {
    protected void compute() {
        TaskBarrier b = new TaskBarrier() {
            protected boolean terminate(int cycle, int registeredParties) {
                System.out.println("Cycle is " + cycle + ";"
                        + registeredParties + " parties");
                return cycle >= 10;
            }
        };
        int n = 3;
        CyclicAction[] actions = new CyclicAction[n];
        for (int i = 0; i < n; ++i) {
            final int index = i;
            actions[i] = new CyclicAction(b) {
                protected void compute() {
                    System.out.println("I'm working " + getCycle() + " "
                            + index);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        }
        for (int i = 0; i < n; ++i)
            actions[i].fork();
        for (int i = 0; i < n; ++i)
            actions[i].join();
    }
}

在 清单 6 中,CyclicAction[] 数组建立了三个任务,打印各自的工作次数和序号。而在 

b.terminate() 方法中,我们设置的中止条件表示重复 10 次计算后中止。现在剩下的工作就是

将 ConcurrentPrint 提交到 ForkJoinPool 了。我们可以在 ForkJoinPool 的构造函数中指定需要的线

程数目,例如 ForkJoinPool(4) 就表明线程池包含 4 个线程。我们在一个 JUnit 的 test 方法

中运行 ConcurrentPrint 的这个循环任务:

清单 7. 运行 ConcurrentPrint 循环任务
@Test
public void testBarrier () throws InterruptedException, ExecutionException {
    ForkJoinTask fjt = new ConcurrentPrint();
    ForkJoinPool fjpool = new ForkJoinPool(4);
    fjpool.submit(fjt);
    fjpool.shutdown();
}

RecursiveTask 和 CyclicAction 两个例子的完整代码如下所示:

清单 8. RecursiveTask 和 CyclicAction 两个例子的完整代码
package tests;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import jsr166y.forkjoin.CyclicAction;
import jsr166y.forkjoin.ForkJoinPool;
import jsr166y.forkjoin.ForkJoinTask;
import jsr166y.forkjoin.RecursiveAction;
import jsr166y.forkjoin.RecursiveTask;
import jsr166y.forkjoin.TaskBarrier;

import org.junit.Test;

class Fibonacci extends RecursiveTask<Integer> {
    final int n;

    Fibonacci(int n) {
        this.n = n;
    }

    private int compute(int small) {
        final int[] results = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
        return results[small];
    }

    public Integer compute() {
        if (n <= 10) {
            return compute(n);
        }
        Fibonacci f1 = new Fibonacci(n - 1);
        Fibonacci f2 = new Fibonacci(n - 2);
        System.out.println("fork new thread for " + (n - 1));
        f1.fork();
        System.out.println("fork new thread for " + (n - 2));
        f2.fork();
        return f1.join() + f2.join();
    }
}

class ConcurrentPrint extends RecursiveAction {
    protected void compute() {
        TaskBarrier b = new TaskBarrier() {
            protected boolean terminate(int cycle, int registeredParties) {
                System.out.println("Cycle is " + cycle + ";"
                        + registeredParties + " parties");
                return cycle >= 10;
            }
        };
        int n = 3;
        CyclicAction[] actions = new CyclicAction[n];
        for (int i = 0; i < n; ++i) {
            final int index = i;
            actions[i] = new CyclicAction(b) {
                protected void compute() {
                    System.out.println("I'm working " + getCycle() + " "
                            + index);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        }
        for (int i = 0; i < n; ++i)
            actions[i].fork();
        for (int i = 0; i < n; ++i)
            actions[i].join();
    }
}

public class TestForkJoin {
    @Test
    public void testBarrier () throws InterruptedException, ExecutionException {
		System.out.println("\ntesting Task Barrier ...");
        ForkJoinTask fjt = new ConcurrentPrint();
        ForkJoinPool fjpool = new ForkJoinPool(4);
        fjpool.submit(fjt);
        fjpool.shutdown();
    }

    @Test
    public void testFibonacci () throws InterruptedException, ExecutionException {
    	System.out.println("\ntesting Fibonacci ...");
		final int num = 14; //For demo only
        ForkJoinTask<Integer> fjt = new Fibonacci(num);
        ForkJoinPool fjpool = new ForkJoinPool();
        Future<Integer> result = fjpool.submit(fjt);

        // do something
        System.out.println("Fibonacci(" + num + ") = " + result.get());
    }
}

运行以上代码,我们可以得到以下结果:

testing Task Barrier ...
I'm working 0 2
I'm working 0 0
I'm working 0 1
Cycle is 0; 3 parties
I'm working 1 2
I'm working 1 0
I'm working 1 1
Cycle is 1; 3 parties
I'm working 2 0
I'm working 2 1
I'm working 2 2
Cycle is 2; 3 parties
I'm working 3 0
I'm working 3 2
I'm working 3 1
Cycle is 3; 3 parties
I'm working 4 2
I'm working 4 0
I'm working 4 1
Cycle is 4; 3 parties
I'm working 5 1
I'm working 5 0
I'm working 5 2
Cycle is 5; 3 parties
I'm working 6 0
I'm working 6 2
I'm working 6 1
Cycle is 6; 3 parties
I'm working 7 2
I'm working 7 0
I'm working 7 1
Cycle is 7; 3 parties
I'm working 8 1
I'm working 8 0
I'm working 8 2
Cycle is 8; 3 parties
I'm working 9 0
I'm working 9 2

testing Fibonacci ...
fork new thread for 13
fork new thread for 12
fork new thread for 11
fork new thread for 10
fork new thread for 12
fork new thread for 11
fork new thread for 10
fork new thread for 9
fork new thread for 10
fork new thread for 9
fork new thread for 11
fork new thread for 10
fork new thread for 10
fork new thread for 9
Fibonacci(14) = 610
 

结论

从以上的例子中可以看到,通过使用 Fork/Join 模式,软件开发人员能够方便地利用多核平台的计

算能力。尽管还没有做到对软件开发人员完全透明,Fork/Join 模式已经极大地简化了编写并发程

序的琐碎工作。对于符合 Fork/Join 模式的应用,软件开发人员不再需要处理各种并行相关事务,

例如同步、通信等,以难以调试而闻名的死锁和 data race 等错误也就不会出现,提升了思考问题

的层次。你可以把 Fork/Join 模式看作并行版本的 Divide and Conquer 策略,仅仅关注如何划分任

务和组合中间结果,将剩下的事情丢给 Fork/Join 框架。

在实际工作中利用 Fork/Join 模式,可以充分享受多核平台为应用带来的免费午餐。

参考资料

学习

  1. Java Fork Join 框架
  2. Oracle官方并发教程之高级并发对象
  3. 定制并发类(八)自定义在 Fork/Join 框架中运行的任务
  4. Java Fork Join 框架(四)性能
  5. Fork and Join: Java也可以轻松地编写并发程序
  6. 分享ppt: java7里的fork-join
  7. Java Fork Join 框架(三)实现
  8. Java Fork Join框架 (二) 简介
  9. 定制并发类(七)实现ThreadFactory接口生成自定义的线程给Fork/Join框架
  10. Oracle官方并发教程之Guarded Blocks
  11. 测试并发应用(四)监控Fork/Join池
  12. Java 7 Concurrency Cookbook – Javier Fernández González -前言
  13. Fork/Join框架(一)引言
  14. 并发性能优化 – 降低锁粒度
  15. Oracle官方并发教程之不可变对象
分享到:
评论

相关推荐

    ForkJoin并发框架入门示例

    介绍了ForkJoin并发框架,供有java基础者学习,工作配合使用,附件带有PPT,介绍并发与并行区别,和ForkJoin代码范例,资源来自网络,分享分享!

    并发编程笔记20190526.docx

    5、Fork/Join框架的异常处理 26 6、Fork/Join框架的实现原理 26 二、闭锁CountDownLatch 28 1、应用场景 28 2、CyclicBarrier 28 3、Semaphore 29 4、Callable、Future和FutureTask 30 5、原子操作CAS (compare ...

    基于JDK的ForkJoin构建一个简单易用的并发组件1

    I.背景实际项中,使并发的个case就是商品详情页的展了,个详情页的展,除了基本的商品数据之外,还有销量,地址,评价,推荐,店铺信息,装饰信息等,段伪代码来描述

    【Java面试系列】JDK 1.8 新特性之 Stream API.pdf

    同时,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。 所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核...

    java8源码-SpringTree:互联网通用技术

    java8 源码 SpringTree springboot mybatis mysql zookeeper ...采用工作窃取模式(当前线程任务执行完成,可窃取其他线程的执行任务),将大任务分解成多个小任务,最后将结果join 7:分布式锁 red

    龙果java并发编程完整视频

    第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器...

    汪文君高并发编程实战视频资源下载.txt

     高并发编程第三阶段34讲 ForkJoin框架之RecursiveAction_.mp4  高并发编程第三阶段35讲 Phaser工具的实战案例使用第一部分_.mp4  高并发编程第三阶段36讲 Phaser工具的实战案例使用第二部分_.mp4  高并发编程第...

    java并发编程

    第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器...

    Java 并发编程原理与实战视频

    第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器...

    龙果 java并发编程原理实战

    第43节Future设计模式实现(实现类似于JDK提供的Future)00:19:20分钟 | 第44节Future源码解读00:29:22分钟 | 第45节Fork/Join框架详解00:28:09分钟 | 第46节同步容器与并发容器00:18:44分钟 | 第47节并发容器...

    jdk11jar架包文件下载

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

    Java并发编程原理与实战

    ForkJoin框架详解.mp4 同步容器与并发容器.mp4 并发容器CopyOnWriteArrayList原理与使用.mp4 并发容器ConcurrentLinkedQueue原理与使用.mp4 Java中的阻塞队列原理与使用.mp4 实战:简单实现消息队列.mp4 并发容器...

    汪文君高并发编程实战视频资源全集

     高并发编程第三阶段34讲 ForkJoin框架之RecursiveAction_.mp4  高并发编程第三阶段35讲 Phaser工具的实战案例使用第一部分_.mp4  高并发编程第三阶段36讲 Phaser工具的实战案例使用第二部分_.mp4  高并发编程第...

    JDK7-Java7-JavaSE7新特性和增强功能-第一部

    1. 介绍JDK7比JDK6新增的Java语言特性.2. 介绍JDK7的库增强功能. 3. 提供使用场景,例子参考说明.4. 熟悉JDK7新特性和库对Android开发(基于Java语言)更加得心应手。 5. 熟悉JDK7新特性能方便进行Java底层开发,比如...

    Java 高并发六:JDK并发包2详解

    本文主要介绍Java高并发这里整理了详细资料,并讲解了 1. 线程池的基本使用 2. 扩展和增强线程池 3. ForkJoin的知识,有兴趣的小伙伴可以参考下

Global site tag (gtag.js) - Google Analytics