
搜了 stackoverflow 似乎没有人提过这个问题,因为我也是迫于公司要初学 java,大家见笑了。
这段代码主要想大家帮忙看看 iterate 函数在 for 循环会不会有线程安全的问题。
主要的问题是:
不肯定 iterate 函数 在 for 循环之中,intA 以及 uuid 会不会有线程安全的问题。 1: 就是 thenApply 里面的 intA 与 supplyAsync 里面的是否一致。 2: 也不肯定这传入的参数 intA 与 uuid, 与 thenApply 里面拿到的会不会都是同一组参数。也就是会不会因为循环而导致 uuid 是拿到较为新的情况,而 intA 比较旧?导致计算结果不合理。
我原本想把结果 print 出来测试一下,但是又无从下手,因为这如果真的有线程安全问题也不是 debugger 能看的出来。
先感谢大家的赐教。谢谢
import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.IntStream; public class Test { public static void main(String[] args) { new Test().run(); } private void run() { List<Integer> list = IntStream.range(1,10000).boxed().collect(Collectors.toList()); List<CompletableFuture<Integer>> cfList = new ArrayList<>(); for (Integer intA: list) { String uuid = UUID.randomUUID().toString(); CompletableFuture<Integer> future = this.iterate(intA,uuid); cfList.add(future); } CompletableFuture<List<Integer>> resultCf = this.allOf(cfList); resultCf.join(); } private CompletableFuture<Integer> iterate(Integer intA, String uuid) { return CompletableFuture.supplyAsync(()->{ // 假设需要用到第一个参数,然后返回 return intA+2; }).thenApply((req)->{ return intA+5; }).thenApply((value)->{ // 假设这个耗时操作要用到第一个参数以及第二个参数。 // 不肯定 在 for 循环之中,intA 以及 uuid 会不会有线程安全的问题。 // 1: 就是 thenApply 里面的 intA 与 supplyAsync 里面的是否一致。 // 2: 也不肯定这传入的参数 intA 与 uuid, 与 thenApply 里面拿到的会不会都是同一组参数。 // 也就是会不会因为循环而导致 uuid 是拿到较为新的情况,而 intA 比较旧?导致计算结果不合理。 System.out.println(uuid); return 3+value; }); } // 等待 list 完毕 private <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futuresList) { CompletableFuture<Void> allFuturesResult = CompletableFuture.allOf(futuresList.toArray(new CompletableFuture[0])); return allFuturesResult.thenApply(v -> futuresList.stream(). map(CompletableFuture::join). collect(Collectors.toList()) ); } } 1 hingbong 2021 年 10 月 22 日 lambda 里面拿外面的变量都是栈里面而且还是 final 的吧,应该没问题 |
2 0Vincent0Zhang0 2021 年 10 月 22 日 via Android 这个场景下,没有线程安全问题。 因为: 1.iterate 方法里没有“修改”intA (给 intA 赋值不算“修改”) 2.list 里面没有相同的 Integer |
3 chendy 2021 年 10 月 22 日 貌似全程没看到会被多个线程访问到的变量,所以是不存在线程安全问题 thenApply 是上一步执行完了执行下一步,是同步的,所以最后一个 thenApply 里能达到的 value 就是最新的 另外不存在 intA 比较旧的问题,intA 全程没有被重新赋值过,一直就是初始值 |
4 wangyu17455 2021 年 10 月 23 日 lambda 里面访问外部的临时变量,是通过构造函数传参实现的,这也是为什么不能在 lamda 和匿名内部类里面修改外部临时变量的值,两者只通过运行构造函数同步一次。所以在 computablefuture 里面虽然运行顺序由你的代码指定,但是你传入的三个 lambda 是在同一次循环内部创建完成的。 |
5 golangLover OP |
6 golangLover OP @wangyu17455 另外想请问这个 “lambda 只通过运行构造函数同步一次” 和 “你传入的三个 lambda 是在同一次循环内部创建完成的” 这里的出处能在哪里找到。我觉得自己对这个了解不是太深入。搜了 lambda 串联好像没提到这个。谢谢 |
7 wangyu17455 2021 年 10 月 25 日 “只通过构造函数同步一次“反编译就可以看到, “你传入的三个 lambda 是在同一次循环内部创建完成的” 把 lambda 当做匿名内部类理解就行,匿名内部类实际上就是正常类的语法糖,()->{xx}在编译的时候就被当做 new X(y)处理 |
8 golangLover OP @wangyu17455 好的,谢谢你! |