【Java&Go并发编程系列】5.运行多个任务并处理第一个结果——invokeAny VS channel

说明:Java & Go 并发编程序列的文章,根据每篇文章的主题或细分标题,分别演示 Java 和 Go 语言当中的相关实现。更多该系列文章请查看:Java & Go 并发编程系列

本文介绍 Java 和 Go 语言中如何实现运行多个并发任务,但只处理第一个已完成任务的结果的场景。

代码场景:假设有一道计算题1×1+2×2+3×3+…+10×10,分发给10个同学计算,谁先计算出来则采用该同学的计算结果。

「Java」ThreadPoolExecutor#invokeAny

该方法接收一个任务列表,然后运行任务,并返回第一个完成的执行结果。

/**
 * CalculationTask 代表执行一个计算任务<br>
 * 传入一个正整数 n 并返回 1×1+2×2+...+(n-1)×(n-1)+n×n
 */
static class CalculationTask implements Callable<Integer> {

    private Integer number;

    public CalculationTask(Integer number) {
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
        // 随机模拟每个同学计算的时间
        int randomMills = ThreadLocalRandom.current().nextInt(500);
        TimeUnit.MILLISECONDS.sleep(randomMills);
        int result = 0;
        for (int i = 1; i <= number; i++) {
            result += i * i;
        }
        // 以下语句只是为了打印计算过程的数学表达式
        String expression =
                IntStream.rangeClosed(1, number).boxed()
                        .map(i -> String.format("%d×%d", i, i))
                        .collect(Collectors.joining("+"));
        System.out.printf("计算: %s = %d\n", expression, result);
        return result;
    }
}

public static void main(String[] args) {

    // 创建一个固定线程数的线程池
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);

    // 生成10个计算任务集合并发执行,代表10个同学参与计算
    List<CalculationTask> list =
            IntStream
                    .rangeClosed(1, 10)
                    .boxed()
                    .map(number -> new CalculationTask(10))
                    .collect(Collectors.toList());
    try {
        // 将计算任务提交给线程池,并返回第一个已完成任务的结果
        Integer result = executor.invokeAny(list);
        System.out.printf("第一个返回的结果: %d\n", result);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    // 关闭线程池
    executor.shutdown();
    System.out.println("End.");

}

运行结果:

计算: 1×1+2×2+3×3+4×4+5×5+6×6+7×7+8×8+9×9+10×10 = 385
第一个返回的结果: 385
End.
「Go」channel

利用缓冲通道来存放不同 Goroutine 的计算结果,从通道中接收第一个计算结果。

func main() {
	// 创建一个容量为10的缓冲通道
	bufferChan := make(chan int, 10)
	for i := 1; i <= 10; i++ {
		// 启用10个 Goroutine 模拟10个同学并发计算
		go func() {
			result := calculation(10)
			// 将计算结果发送到通道
			bufferChan <- result
		}()
	}
	fmt.Printf("第一个返回的结果: %d\n", <-bufferChan)
	fmt.Println("End.")
}

// 传入一个正整数 n 并返回 1×1+2×2+...+(n-1)×(n-1)+n×n
func calculation(number int) int {
	// 随机模拟每个同学计算的时间
	randomMills := rand.Intn(500)
	time.Sleep(time.Millisecond * time.Duration(randomMills))
	var result int
	for i := 1; i <= number; i++ {
		result += i * i
	}
	fmt.Printf("计算: 1×1+2×2+...+%d×%d = %d\n", number, number, result)
	return result
}

运行结果:

计算: 1×1+2×2+...+10×10 = 385
第一个返回的结果: 385
End.
拓展

以上数学运算可以抽象为 map 和 reduce。

用 Python 实现如下:

from functools import reduce

li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
total = reduce(lambda x, y: x + y, map(lambda x: x * x, li))
print(total)  # 打印385

用 Java 实现如下:

Integer total = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
        .map(x -> x * x)
        .reduce((x, y) -> x + y)
        .get();
System.out.println(total); // 打印385

更多该系列文章请查看:Java & Go 并发编程系列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值