三、Java8的CompletableFuture,Java的多執行緒開發
1、CompletableFuture的常用方法
- 以后用到再加
runAsync() :開啟異步(創建執行緒執行任務),無回傳值
supplyAsync() :開啟異步(創建執行緒執行任務),有回傳值
thenApply() :然后應用,適用于有回傳值的結果,拿著回傳值再去處理,
exceptionally():用于處理異步任務執行程序中出現例外的情況的一個方法:回傳默認值或者一個替代的 CompletableFuture 物件,從而避免系統的崩潰或例外處理的問題,
handle():類似exceptionally()
get() :阻塞執行緒:主要可以: ①獲取執行緒中的例外然后處理例外、②設定等待時間
join() :阻塞執行緒:推薦使用 join() 方法,因為它沒有受到 interrupt 的干擾,不需要捕獲例外,也不需要強制型別轉換,他自己會拋出例外,
CompletableFuture.allOf()
CompletableFuture.anyOf()
- get() 和 join() 方法區別?
- 都可以阻塞執行緒 —— 等所有任務都執行完了再執行后續代碼,
CompletableFuture 中的 get() 和 join() 方法都用于獲取異步任務的執行結果,但是在使用時需要注意以下幾點區別:
1. 拋出例外的方式不同:如果異步任務執行程序中出現例外, get() 方法會拋出 ExecutionException 例外,而 join() 方法會拋出 CompletionException 例外,這兩個例外都是繼承自 RuntimeException 的,
2. 方法呼叫限制不同: join() 方法是不可以被中斷的,一旦呼叫就必須等待任務執行完成才能回傳結果;而 get() 方法可以在呼叫時設定等待的超時時間,如果超時還沒有獲取到結果,就會拋出 TimeoutException 例外,
3. 回傳結果型別不同: get() 方法回傳的是異步任務的執行結果,該結果是泛型型別 T 的,需要強制轉換才能獲取真正的結果;而 join() 方法回傳的是異步任務的執行結果,該結果是泛型型別 T,不需要強制轉換,
4. 推薦使用方式不同:推薦在 CompletableFuture 中使用 join() 方法,因為它沒有受到 interrupt 的干擾,不需要捕獲例外,也不需要強制型別轉換,
綜上所述, get() 方法和 join() 方法都是獲取異步任務的執行結果,但是在使用時需要根據具體場景選擇使用哪個方法,如果需要獲取執行結果并且不希望被中斷,推薦使用 join() 方法;如果需要控制等待時間或者需要捕獲例外,則可以使用 get() 方法,
- anyOf() 和 allOf() 的區別?
CompletableFuture 是 Java 8 引入的一個強大的異步編程工具,它支持鏈式呼叫、組合和轉換異步操作等功能,其中,anyOf 和 allOf 都是 CompletableFuture 的兩個常用方法,它們的區別如下:
1. anyOf:任意一個 CompletableFuture 完成,它就會跟隨這個 CompletableFuture 的結果完成,回傳第一個完成的 CompletableFuture 的結果,
2. allOf:所有的 CompletableFuture 都完成時,它才會跟隨它們的結果完成,回傳一個空的 CompletableFuture,
簡而言之,anyOf 和 allOf 的最大區別是:anyOf 任意一個 CompletableFuture 完成就跟著它的結果完成,而 allOf 所有的 CompletableFuture 完成才可以完成,并回傳一個空的 CompletableFuture,
舉例來說,如果有三個 CompletableFuture:f1、f2、f3,其中 f1 和 f2 可能會回傳一個字串,而 f3 可能會回傳一個整數,那么:
- anyOf(f1, f2, f3) 的結果是 f1、f2、f3 中任意一個 CompletableFuture 的結果;
- allOf(f1, f2, f3) 的結果是一個空的 CompletableFuture,它的完成狀態表示 f1、f2、f3 是否全部完成,
總之,anyOf 和 allOf 在實際使用中可以根據不同的需求來選擇,它們都是 CompletableFuture 中非常強大的組合操作,
2、使用CompletableFuture
2.1、物體類準備
package com.cc.md.entity;
import lombok.Data;
/**
* @author CC
* @since 2023/5/24 0024
*/
@Data
public class UserCs {
private String name;
private Integer age;
}
2.2、常用方式
- 無回傳值推薦:開啟多執行緒——無回傳值的——阻塞:test06
@Resource(name = "myIoThreadPool")
private ThreadPoolTaskExecutor myIoThreadPool;
//CompletableFuture開啟多執行緒——無回傳值的
@Test
public void test06() throws Exception {
List<CompletableFuture<Void>> futures = new ArrayList<>();
//回圈,模仿很多任務
for (int i = 0; i < 1000; i++) {
int finalI = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//第一批創建的執行緒數
log.info("列印:{}", finalI);
//模仿io流耗時
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, myIoThreadPool);
futures.add(future);
}
//阻塞:多執行緒的任務執行,相當于多執行緒執行完了,再執行后面的代碼
//如果不阻塞,上面的相當于異步執行了,
//阻塞方式1:可以獲取回傳的例外、設定等待時間
// futures.forEach(future -> {
// try {
// future.get();
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// });
//阻塞方式2(推薦)
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
log.info("列印:都執行完了,,,");
}
- 有回傳值推薦:開啟多執行緒——有回傳值的,回傳一個新的List——阻塞——使用stream流的map:test09
- test07、test08 可以轉化為 test09 (現在這個)
- 可以回傳任務型別的值,不一定要回傳下面的user物件,
@Resource(name = "myIoThreadPool")
private ThreadPoolTaskExecutor myIoThreadPool;
//CompletableFuture開啟多執行緒——有回傳值的,回傳一個新的List——先有資料的情況——使用stream流的map
//像這種,需要構建另一個陣列的,相當于一個執行緒執行完了,會有回傳值
//使用stream流的map + CompletableFuture.supplyAsync()
@Test
public void test09() throws Exception {
//先獲取資料,需要處理的任務,
List<UserCs> users = this.getUserCs();
//莫法處理任務
List<CompletableFuture<UserCs>> futures = users.stream()
.map(user -> CompletableFuture.supplyAsync(() -> {
// 處理資料
user.setName(user.getName() + "-改");
log.info("列印-改:{}", user.getName());
// 其他的業務邏輯,,,
return user;
}, myIoThreadPool)).collect(Collectors.toList());
//獲取futures
List<UserCs> endList = futures.stream()
//阻塞所有執行緒
.map(CompletableFuture::join)
//取age大于10的用戶
.filter(user -> user.getAge() > 10)
//按照age升序排序
.sorted(Comparator.comparing(UserCs::getAge))
.collect(Collectors.toList());
log.info("列印:都執行完了,,,{}", endList);
}
2.3、例外處理
- exceptionally
- handle
//CompletableFuture 例外處理
@Test
public void test10() throws Exception {
//先獲取資料,需要處理的任務,
List<UserCs> users = this.getUserCs();
//莫法處理任務
List<CompletableFuture<UserCs>> futures = users.stream()
.map(user -> CompletableFuture.supplyAsync(() -> {
if (user.getAge() > 5){
int a = 1/0;
}
// 處理資料
user.setName(user.getName() + "-改");
log.info("列印-改:{}", user.getName());
// 其他的業務邏輯,,,
return user;
}, myIoThreadPool)
//處理例外方式1:回傳默認值或者一個替代的 Future 物件,從而避免系統的崩潰或例外處理的問題,
.exceptionally(throwable -> {
//可以直接獲取user
System.out.println("例外了:" + user);
//處理例外的方法……
//1還可以進行業務處理……比如將例外資料存起來,然后匯出……
//2回傳默認值,如:user、null
//return user;
//3拋出例外
throw new RuntimeException(throwable.getMessage());
})
//處理例外方式2:類似exceptionally(不推薦)
// .handle((userCs, throwable) -> {
// System.out.println("handle:" + user);
// if (throwable != null) {
// // 處理例外
// log.error("處理用戶資訊出現例外,用戶名為:" + user.getName(), throwable);
// // 回傳原始資料
// return userCs;
// } else {
// // 回傳正常資料
// return userCs;
// }
// })
)
.collect(Collectors.toList());
//獲取futures
List<UserCs> endList = futures.stream()
//阻塞所有執行緒
.map(CompletableFuture::join)
//取age大于10的用戶
.filter(user -> user.getAge() > 10)
//按照age升序排序
.sorted(Comparator.comparing(UserCs::getAge))
.collect(Collectors.toList());
log.info("列印:都執行完了,,,{}", endList);
}
2.4、CompletableFuture的使用測驗
1、推薦使用:test03、test05、test09、test10、test11
2、test07、test08就是test09的前身,
-
test01:獲取當前電腦(服務器)的cpu核數
-
test02:執行緒池原始的使用(不推薦直接這樣用)
-
test03:開啟異步1 —— @Async
-
test04:開啟異步2 —— CompletableFuture.runAsync()
-
test05:開啟異步2的改造 —— CompletableFuture.runAsync() 和 supplyAsync() —— 阻塞所有異步方法,一起提交
-
相當于開了3個執行緒去執行三個不同的方法,然后執行完后一起提交,
-
-
test052:開啟異步2的改造 —— 第一個任務執行完了,獲取到回傳值,給后面的執行,可以連寫,也可以單寫, —— 阻塞執行緒:get、join
-
test06:CompletableFuture開啟多執行緒——無回傳值的
-
test07:CompletableFuture開啟多執行緒——無回傳值的——構建一個新List
-
1、相當于多執行緒執行任務,然后把結果插入到List中 2、接收多執行緒的List必須是執行緒安全的,ArrayList執行緒不安全 執行緒安全的List —— CopyOnWriteArrayList 替代 ArrayList
-
-
test08:CompletableFuture開啟多執行緒——無回傳值的——構建一個新List——先有資料的情況(基本和test07是一個方法)
-
test09:CompletableFuture開啟多執行緒——有回傳值的,回傳一個新的List——先有資料的情況——使用stream流的map
-
test10:CompletableFuture 例外處理,相當于是 test09的增強,處理例外
-
test11:CompletableFuture 例外處理:如果出現例外就舍棄任務,
-
1、想了一下,出現例外后的任務確實沒有執行下去了,任務不往下執行,怎么會發現例外呢? 2、發現了例外任務也就完了,而且列印了例外,相當于回傳了例外, 3、未發生例外的任務會執行完成,如果發生例外都回傳空,最后舍棄空的,就得到任務執行成功的 CompletableFuture
-
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓所有方式↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
package com.cc.md;
import com.cc.md.entity.UserCs;
import com.cc.md.service.IAsyncService;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@SpringBootTest
class Test01 {
private static final Logger log = LoggerFactory.getLogger(Test01.class);
@Resource(name = "myIoThreadPool")
private ThreadPoolTaskExecutor myIoThreadPool;
/**
* 異步類
*/
@Resource
private IAsyncService asyncService;
@Test
void test01() {
//獲取當前jdk能呼叫的CPU個數(當前服務器的處理器個數)
int i = Runtime.getRuntime().availableProcessors();
System.out.println(i);
}
//執行緒池原始的使用
@Test
void test02() {
try {
for (int i = 0; i < 1000; i++) {
int finalI = i;
myIoThreadPool.submit(() -> {
//第一批創建的執行緒數
log.info("列印:{}", finalI);
//模仿io流耗時
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
}catch(Exception e){
throw new RuntimeException(e);
}finally {
myIoThreadPool.shutdown();
}
}
//開啟異步1 —— @Async
@Test
public void test03() throws Exception {
log.info("列印:{}", "異步測驗的-主方法1");
asyncService.async1();
asyncService.async2();
//不會等待異步方法執行,直接回傳前端資料
log.info("列印:{}", "異步測驗的-主方法2");
}
//開啟異步2 —— CompletableFuture.runAsync()
@Test
public void test04() throws Exception {
log.info("列印:{}", "異步測驗的-主方法1");
CompletableFuture.runAsync(() -> {
log.info("列印:{}", "異步方法1!");
//異步執行的代碼,也可以是方法,該方法不用單獨寫到其他類中,
this.async2("異步方法1!-end");
}, myIoThreadPool);
//不會等待異步方法執行,直接回傳前端資料
log.info("列印:{}", "異步測驗的-主方法2");
}
//異步需要執行的方法,可以寫在同一個類中,
private void async2(String msg) {
//模仿io流耗時
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("列印:{}", msg);
}
//開啟異步2的改造 —— CompletableFuture.runAsync() 和 supplyAsync() —— 阻塞所有異步方法,一起提交
//相當于開了3個執行緒去執行三個不同的方法,然后執行完后一起提交,
@Test
public void test05() throws Exception {
log.info("列印:{}", "異步測驗的-主方法1");
//異步執行1
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
log.info("列印:{}", "異步方法1!");
//異步執行的代碼,也可以是方法,該方法不用單獨寫到其他類中,
this.async2("異步方法1-end");
return "異步方法1";
}, myIoThreadPool);
//異步執行2
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
log.info("列印:{}", "異步方法2!");
//異步執行的代碼,也可以是方法,該方法不用單獨寫到其他類中,
this.async2("異步方法2-end");
return "異步方法2";
}, myIoThreadPool);
//異步執行3,不用我們自己的執行緒池 —— 用的就是系統自帶的 ForkJoinPool 執行緒池
CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
log.info("列印:{}", "異步方法3!");
//異步執行的代碼,也可以是方法,該方法不用單獨寫到其他類中,
this.async2("異步方法3-end");
});
//阻塞所有異步方法,一起提交后才走下面的代碼
CompletableFuture.allOf(future1, future2, future3).join();
log.info("列印:{}", "異步-阻塞-測驗的-主方法2-end");
}
//開啟異步2的改造 —— 第一個任務執行完了,獲取到回傳值,給后面的執行,可以連寫,也可以單寫, —— 阻塞執行緒:get、join
// CompletableFuture 的 get 和 join 方法區別:
// get:①可以獲取執行緒中的例外、②設定等待時間
// join:推薦在 CompletableFuture 中使用 join() 方法,因為它沒有受到 interrupt 的干擾,不需要捕獲例外,也不需要強制型別轉換,
@Test
public void test052() throws Exception {
log.info("列印:{}", "異步測驗的-主方法1");
//異步執行1
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
log.info("列印:{}", "異步方法1!");
// 異步執行的代碼,也可以是方法,該方法不用單獨寫到其他類中,
String str = "異步方法1-end";
this.async2(str);
return str;
}, myIoThreadPool);
// 異步執行2 - 無回傳值 —— 分開寫的方式
CompletableFuture<Void> future2 = future1.thenAccept(str1 -> {
log.info("列印:{}", "異步方法2!");
// 異步執行的代碼,也可以是方法,該方法不用單獨寫到其他類中,
this.async2(String.format("%s-加-異步方法2! - 無回傳值 - ",str1));
});
// 異步執行3 - 有回傳值 —— 分開寫future1,連寫future3方式
CompletableFuture<String> future3 = future1.thenApply(str2 -> {
log.info("列印:{}", "異步方法3!");
// 異步執行的代碼,也可以是方法,該方法不用單獨寫到其他類中,
this.async2(String.format("%s-加-異步方法3! - 有回傳值 - ", str2));
return "異步執行3 - 有回傳值 ";
//連寫的方式,
}).thenApply(str3 -> {
String format = String.format("%s- end", str3);
log.error("異步3然后應用 - {}", format);
//回傳后面的應用
return format;
});
// 獲取future3的回傳值:
//如果需要捕獲例外、設定等待超時時間,則用get
log.info("future3的回傳值(不阻塞):{}", future3.get());
// log.info("future3的回傳值(不阻塞-設定等待時間,超時報錯:TimeoutException):{}",
// future3.get(2, TimeUnit.SECONDS));
//推薦使用 join方法
// log.info("future3的回傳值(阻塞):{}", future3.join());
//阻塞所有異步方法,一起提交后才走下面的代碼
CompletableFuture.allOf(future1, future2).join();
log.info("列印:{}", "異步-阻塞-測驗的-主方法2-end");
}
//CompletableFuture開啟多執行緒——無回傳值的
@Test
public void test06() throws Exception {
List<CompletableFuture<Void>> futures = new ArrayList<>();
//回圈,模仿很多任務
for (int i = 0; i < 1000; i++) {
int finalI = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//第一批創建的執行緒數
log.info("列印:{}", finalI);
//模仿io流耗時
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, myIoThreadPool);
futures.add(future);
}
//阻塞:多執行緒的任務執行,相當于多執行緒執行完了,再執行后面的代碼
//如果不阻塞,上面的相當于異步執行了,
//阻塞方式1:可以獲取回傳的例外、設定等待時間
// futures.forEach(future -> {
// try {
// future.get();
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// });
//阻塞方式2(推薦)
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
log.info("列印:都執行完了,,,");
}
//CompletableFuture開啟多執行緒——無回傳值的——構建一個新List
//相當于多執行緒執行任務,然后把結果插入到List中
//接收多執行緒的List必須是執行緒安全的,ArrayList執行緒不安全
//執行緒安全的List —— CopyOnWriteArrayList 替代 ArrayList
@Test
public void test07() throws Exception {
List<CompletableFuture<Void>> futures = new ArrayList<>();
//存資料的List
List<UserCs> addList = new CopyOnWriteArrayList<>();
//回圈,模仿很多任務
for (int i = 0; i < 1000; i++) {
int finalI = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
log.info("列印:{}", finalI);
UserCs userCs = new UserCs();
userCs.setName(String.format("姓名-%s", finalI));
userCs.setAge(finalI);
addList.add(userCs);
}, myIoThreadPool);
futures.add(future);
}
//阻塞
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
//回傳新的List:endList,取age大于10的用戶
List<UserCs> endList = addList.stream()
.filter(user -> user.getAge() > 10)
//按照age升序排序
.sorted(Comparator.comparing(UserCs::getAge))
.collect(Collectors.toList());
log.info("列印:都執行完了,,,{}", endList);
}
//CompletableFuture開啟多執行緒——無回傳值的——構建一個新List——先有資料的情況
//用CopyOnWriteArrayList 替代 ArrayList接收
@Test
public void test08() throws Exception {
//先獲取資料,需要處理的任務,
List<UserCs> users = this.getUserCs();
//開啟多執行緒
List<CompletableFuture<Void>> futures = new ArrayList<>();
//存資料的List
List<UserCs> addList = new CopyOnWriteArrayList<>();
//莫法處理任務
users.forEach(user -> {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//添加資料
user.setName(user.getName() + "-改");
addList.add(user);
log.info("列印-改:{}", user.getName());
//其他的業務邏輯,,,
}, myIoThreadPool);
futures.add(future);
});
//阻塞
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
//回傳新的List:endList
List<UserCs> endList = addList.stream()
.filter(user -> user.getAge() > 10)
//按照age升序排序
.sorted(Comparator.comparing(UserCs::getAge))
.collect(Collectors.toList());
log.info("列印:都執行完了,,,{}", endList);
}
//CompletableFuture開啟多執行緒——有回傳值的,回傳一個新的List——先有資料的情況——使用stream流的map
//像這種,需要構建另一個陣列的,相當于一個執行緒執行完了,會有回傳值
//使用stream流的map + CompletableFuture.supplyAsync()
@Test
public void test09() throws Exception {
//先獲取資料,需要處理的任務,
List<UserCs> users = this.getUserCs();
//莫法處理任務
List<CompletableFuture<UserCs>> futures = users.stream()
.map(user -> CompletableFuture.supplyAsync(() -> {
// 處理資料
user.setName(user.getName() + "-改");
log.info("列印-改:{}", user.getName());
// 其他的業務邏輯,,,
return user;
}, myIoThreadPool)).collect(Collectors.toList());
//獲取futures
List<UserCs> endList = futures.stream()
//阻塞所有執行緒
.map(CompletableFuture::join)
//取age大于10的用戶
.filter(user -> user.getAge() > 10)
//按照age升序排序
.sorted(Comparator.comparing(UserCs::getAge))
.collect(Collectors.toList());
log.info("列印:都執行完了,,,{}", endList);
}
//基礎資料
private List<UserCs> getUserCs() {
List<UserCs> users = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
UserCs userCs = new UserCs();
userCs.setName(String.format("姓名-%s", i));
userCs.setAge(i);
users.add(userCs);
}
return users;
}
//CompletableFuture 例外處理
@Test
public void test10() throws Exception {
//先獲取資料,需要處理的任務,
List<UserCs> users = this.getUserCs();
//莫法處理任務
List<CompletableFuture<UserCs>> futures = users.stream()
.map(user -> CompletableFuture.supplyAsync(() -> {
if (user.getAge() > 5){
int a = 1/0;
}
// 處理資料
user.setName(user.getName() + "-改");
log.info("列印-改:{}", user.getName());
// 其他的業務邏輯,,,
return user;
}, myIoThreadPool)
//處理例外方式1:回傳默認值或者一個替代的 Future 物件,從而避免系統的崩潰或例外處理的問題,
.exceptionally(throwable -> {
//可以直接獲取user
System.out.println("例外了:" + user);
//處理例外的方法……
//1還可以進行業務處理……比如將例外資料存起來,然后匯出……
//2回傳默認值,如:user、null
//return user;
//3拋出例外
throw new RuntimeException(throwable.getMessage());
})
//處理例外方式2:類似exceptionally(不推薦)
// .handle((userCs, throwable) -> {
// System.out.println("handle:" + user);
// if (throwable != null) {
// // 處理例外
// log.error("處理用戶資訊出現例外,用戶名為:" + user.getName(), throwable);
// // 回傳原始資料
// return userCs;
// } else {
// // 回傳正常資料
// return userCs;
// }
// })
)
.collect(Collectors.toList());
//獲取futures
List<UserCs> endList = futures.stream()
//阻塞所有執行緒
.map(CompletableFuture::join)
//取age大于10的用戶
.filter(user -> user.getAge() > 10)
//按照age升序排序
.sorted(Comparator.comparing(UserCs::getAge))
.collect(Collectors.toList());
log.info("列印:都執行完了,,,{}", endList);
}
//CompletableFuture 例外處理:如果出現例外就舍棄任務,
// 想了一下,出現例外后的任務確實沒有執行下去了,任務不往下執行,怎么會發現例外呢?
// 發現了例外任務也就完了,而且列印了例外,相當于回傳了例外,
// 未發生例外的任務會執行完成,如果發生例外都回傳空,最后舍棄空的,就得到任務執行成功的 CompletableFuture
@Test
public void test11() {
List<UserCs> users = getUserCs();
List<CompletableFuture<UserCs>> futures = users.stream()
.map(user -> CompletableFuture.supplyAsync(() -> {
if (user.getAge() > 15) {
int a = 1 / 0;
}
user.setName(user.getName() + "-改");
log.info("列印-改:{}", user.getName());
return user;
}, myIoThreadPool)
//處理例外
.exceptionally(throwable -> {
//其他處理例外的邏輯
return null;
})
)
//舍棄回傳的物件是null的 CompletableFuture
.filter(e -> Objects.nonNull(e.join())).collect(Collectors.toList());
//獲取futures
List<UserCs> endList = futures.stream()
//阻塞所有執行緒
.map(CompletableFuture::join)
//取age大于10的用戶
.filter(user -> user.getAge() > 10)
//按照age升序排序
.sorted(Comparator.comparing(UserCs::getAge))
.collect(Collectors.toList());
log.info("列印:都執行完了,,,{}", endList);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553404.html
標籤:其他
上一篇:Maven的核心解壓與配置
下一篇:返回列表
