個人網站:www.gydblog.com
01、簡介
FutureTask的get()方法在Future計算完成之前會一直處于阻塞狀态下,isDone()方法容易耗費CPU資源,對于真正的異步處理我們是希望能通過傳入回調函數,在Future結束時自動調用該回調函數,這樣,我們就不用等待結果。阻塞的方式和異步程式設計的設計理念相違背,而輪詢的方式也會耗費CPU資源。是以JDK8中出現了一種新的工具類:CompletableFuture。
CompletableFuture是FutureTask的增強版,提供的是一種類似觀察者模式的機制,可以讓任務執行完成後通知監聽的一方。在任務執行完成之前,監聽方可以去幹别的事情。
在Java8中,CompletableFuture提供了非常強大的Future的擴充功能,可以幫助我們簡化異步程式設計的複雜性,并且提供了函數式程式設計的能力,可以通過回調的方式處理計算結果,也提供了轉換群組合CompletableFuture的方法。它能代表一個明确完成的Future,也有可能代表一個完成階段(CompletableStage)。它支援在計算完成後觸發一些函數或者執行某些動作。它實作了Future和CompletionStage接口。
CompletionStage代表異步計算過程中的某一個階段,一個階段完成以後可能會觸發另外一個階段。
一個階段的計算可以是一個Function,Consumer或者Runnable,比如:stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println());
一個階段的執行可能是被單個階段的完成觸發,也可能是由多個階段一起觸發
02、四大靜态方法入門
java不推薦使用構造方法構造CompletableFuture,推薦使用下面的四大靜态方法。
runAsync無傳回值(預設線程池)
public static CompletableFuture<Void> runAsync(Runnable runnable);
runAsync無傳回值(自定義線程池)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor);
supplyAsync有傳回值(預設線程池)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
supplyAsync有傳回值(自定義線程池)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor);
源碼測試
package com.gyd;
import java.util.concurrent.*;
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("======CompletableFuture.runAsync的示範(無傳回值,預設線程池)");
runAsync1();
System.out.println("======CompletableFuture.runAsync的示範(無傳回值,自定義線程池)");
runAsync2();
System.out.println("======CompletableFuture.supplyAsync1的示範(有傳回值,預設線程池)");
supplyAsync1();
System.out.println("======CompletableFuture.supplyAsync1的示範(有傳回值,自定義線程池)");
supplyAsync2();
}
public static void runAsync1() throws ExecutionException, InterruptedException {
//不推薦
//CompletableFuture completableFuture = new CompletableFuture();
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
try {TimeUnit.SECONDS.sleep(1);}catch (Exception e) {e.printStackTrace();}
});
System.out.println(completableFuture.get());
}
public static void runAsync2() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
try {TimeUnit.SECONDS.sleep(1);}catch (Exception e) {e.printStackTrace();}
},executorService);
System.out.println(completableFuture.get());
executorService.shutdown();
}
public static void supplyAsync1() throws ExecutionException, InterruptedException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() ->{
System.out.println(Thread.currentThread().getName());
try {TimeUnit.SECONDS.sleep(1);}catch (Exception e) {e.printStackTrace();}
return "Hello Supply";
});
System.out.println(completableFuture.get());
}
public static void supplyAsync2() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() ->{
System.out.println(Thread.currentThread().getName());
try {TimeUnit.SECONDS.sleep(1);}catch (Exception e) {e.printStackTrace();}
return "Hello Supply";
},executorService);
System.out.println(completableFuture.get());
executorService.shutdown();
}
}
輸出結果:
======CompletableFuture.runAsync的示範(無傳回值,預設線程池)
ForkJoinPool.commonPool-worker-25
null
======CompletableFuture.runAsync的示範(無傳回值,自定義線程池)
pool-1-thread-1
null
======CompletableFuture.supplyAsync1的示範(有傳回值,預設線程池)
ForkJoinPool.commonPool-worker-25
Hello Supply
======CompletableFuture.supplyAsync1的示範(有傳回值,自定義線程池)
pool-2-thread-1
Hello Supply
Process finished with exit code 0
03、常用方法
獲得結果和觸發計算
package com.gyd;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CompletableFutureDemo4 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() ->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "abc";
});
//不見不散
// System.out.println(completableFuture.get());
//過時不候(抛出TimeoutException)
// System.out.println(completableFuture.get(2,TimeUnit.SECONDS));
//立即傳回(立即擷取結果不阻塞,沒有計算完成的情況 給一個預設值)
// System.out.println(completableFuture.getNow("xxx"));
//complete方法用于判斷是否執行完成,未執行完成則傳回預設值,注意該方法隻能被執行一次
TimeUnit.SECONDS.sleep(4);
System.out.println(completableFuture.complete("completeValue")+" "+completableFuture.join());
}
}
對計算結果進行處理
thenApply:計算結果存在依賴,不同步驟的線程執行串行化,若某個步驟發生異常,則不進入下一步驟并直接進入異常處理流程。
package com.gyd;
import java.util.concurrent.*;
public class CompletableFutureDemo5 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() ->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("111");
return 1;
},executorService).thenApply(f ->{
System.out.println("222");
//若目前步驟有異常,則不會繼續執行後續步驟,直接進入異常處理流程exceptionally
return f+2;
}).thenApply(f->{
System.out.println("333");
return f+3;
}).whenComplete((v,e) ->{
if (e == null) System.out.println("v: "+v);
}).exceptionally( e ->{
//發生異常時的處理
e.printStackTrace();
System.out.println("發生異常");
return null;
});
System.out.println(Thread.currentThread().getName()+"先去忙别的事情");
}
}
handle:計算結果存在依賴,不同步驟的線程執行串行化,若某個步驟發生異常,攜帶異常資訊繼續執行下一步驟。
package com.gyd;
import java.util.concurrent.*;
public class CompletableFutureDemo6 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() ->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("111");
return 1;
},executorService).handle((f,e) ->{
System.out.println("222");
//若目前步驟有異常,則攜帶異常資訊繼續執行後續步驟
int i = 10/0;
return f+2;
}).handle((f,e)->{
System.out.println("上一步驟異常資訊:"+e);
System.out.println("333");
return f+3;
}).whenComplete((v,e) ->{
if (e == null) System.out.println("v: "+v);
}).exceptionally( e ->{
//發生異常時的處理
e.printStackTrace();
System.out.println("發生異常");
return null;
});
System.out.println(Thread.currentThread().getName()+"先去忙别的事情");
}
}
對計算結果進行消費
thenAccept: 任務A執行完繼續執行任務B,任務B需要依賴任務A的計算結果,但任務B無傳回值
package com.gyd;
import java.util.concurrent.*;
public class CompletableFutureDemo7 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() ->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("111");
return 1;
},executorService).thenApply(f ->{
System.out.println("222");
return f+2;
}).thenApply(f->{
return f+3;
}).thenAccept(f -> {
//接受任務的計算結果,進行消費處理,無傳回結果
System.out.println("完成前兩個步驟的任務,消費結果="+f);
});
executorService.shutdown();
}
}
thenRun: 任務A執行完繼續執行任務B,任務B不需要任務A的計算結果
package com.gyd;
import java.util.concurrent.*;
public class CompletableFutureDemo8 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() ->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("111");
return 1;
},executorService).thenApply(f ->{
System.out.println("222");
return f+2;
}).thenApply(f->{
return f+3;
}).thenRun(() ->{
System.out.println("執行完前兩個步驟後,繼續執行目前步驟");
});
executorService.shutdown();
}
}
比較哪個步驟的任務執行快
applyToEither: 可以用來選出執行速度快的步驟。
package com.gyd;
import java.util.concurrent.*;
public class CompletableFutureDemo10 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> playA = CompletableFuture.supplyAsync(() ->{
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
return "playA";
});
CompletableFuture<String> playB = CompletableFuture.supplyAsync(() ->{
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}
return "playB";
});
CompletableFuture<String> winer = playA.applyToEither(playB,f -> f+" is winer");
System.out.println(Thread.currentThread().getName()+"\t"+"-----:" + winer.join());
}
}
對多個步驟的任務結果進行合并輸出
thenCombine: 等待多個CompletionStage任務都完成後,最終把多個任務的結果合并處理輸出。
示例将兩個CompletionStage結果進行合并輸出:
package com.gyd;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CompletableFutureDemo11 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() ->{
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
return "task1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() ->{
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}
return "task2";
});
CompletableFuture<String> future3 = future1.thenCombine(future2, (x , y) -> {
return "合并:"+x+y;
});
System.out.println(future3.join());
}
}
等待多個并行任務執行完成後傳回
比如需要擷取完整訂單資料,可能需要填充訂單的付款資訊、位址資訊、商品資訊,這三部分資料都來自不同系統,互相沒有依賴,可以并行擷取。
package com.gyd;
import java.util.Map;
import java.util.concurrent.*;
public class CompletableFutureDemo12 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
Map<String,Object> orderMap = new ConcurrentHashMap<>();
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() ->{
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("擷取位址資訊...");
orderMap.put("address","湖南");
return "task1";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() ->{
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("擷取支付資訊...");
orderMap.put("payinfo","10元");
return "task2";
});
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() ->{
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("擷取商品資訊...");
orderMap.put("goods","襪子");
return "task3";
});
CompletableFuture<Void> result = CompletableFuture.allOf(future1,future2,future3);
result.join();
System.out.println("完整的訂單資料:"+orderMap.toString());
}
}
04、線程池的運作選擇
廢話少說,直接上代碼:
package com.gyd;
import java.util.concurrent.*;
public class CompletableFutureDemo9 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
//a.模拟任務執行速度快的情況,系統底層會使用main線程處理任務
//運作結果:
//1号任務 ForkJoinPool.commonPool-worker-25
//2号任務 ForkJoinPool.commonPool-worker-25
//3号任務 main
//4号任務 main
System.out.println("===模拟任務執行速度快的情況,系統底層會使用main線程處理任務====");
test0();
//b.使用内置預設線程池+thenRun執行任務
//運作結果:
//1号任務 ForkJoinPool.commonPool-worker-25
//2号任務 ForkJoinPool.commonPool-worker-25
//3号任務 ForkJoinPool.commonPool-worker-25
//4号任務 ForkJoinPool.commonPool-worker-25
//null
System.out.println("===使用内置預設線程池+thenRun執行任務====");
test1();
//c.使用自定義線程池+thenRun執行任務
//運作結果:
//1号任務 pool-1-thread-1
//2号任務 pool-1-thread-1
//3号任務 pool-1-thread-1
//4号任務 pool-1-thread-1
System.out.println("===使用自定義線程池+thenRun執行任務====");
test2();
//d.使用内置預設線程池+thenRunAsync執行任務
//運作結果:
//1号任務 ForkJoinPool.commonPool-worker-25
//2号任務 ForkJoinPool.commonPool-worker-25
//3号任務 ForkJoinPool.commonPool-worker-25
//4号任務 ForkJoinPool.commonPool-worker-25
System.out.println("===使用内置預設線程池+thenRunAsync執行任務====");
test3();
//e.使用自定義線程池+thenRunAsync執行任務
//運作結果:
//1号任務 pool-2-thread-1
//2号任務 ForkJoinPool.commonPool-worker-25
//3号任務 ForkJoinPool.commonPool-worker-25
//4号任務 ForkJoinPool.commonPool-worker-25
System.out.println("===使用自定義線程池+thenRunAsync執行任務====");
test4();
}
//a.使用内置預設線程池+thenRun執行任務
private static void test1() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() ->{
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("1号任務"+"\t"+Thread.currentThread().getName());
return "abcd";
}).thenRun(() ->{
try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("2号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("3号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("4号任務"+"\t"+Thread.currentThread().getName());
});
System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));
System.out.println("===============");
}
//b.使用自定義線程池+thenRun執行任務
private static void test2() throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() ->{
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("1号任務"+"\t"+Thread.currentThread().getName());
return "abcd";
},executorService).thenRun(() ->{
try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("2号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("3号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("4号任務"+"\t"+Thread.currentThread().getName());
});
System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));
executorService.shutdown();
System.out.println("===============");
}
//c.使用内置預設線程池+thenRunAsync執行任務
private static void test3() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() ->{
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("1号任務"+"\t"+Thread.currentThread().getName());
return "abcd";
}).thenRunAsync(() ->{
try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("2号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("3号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("4号任務"+"\t"+Thread.currentThread().getName());
});
System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));
System.out.println("===============");
}
//d.使用自定義線程池+thenRunAsync執行任務
private static void test4() throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() ->{
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("1号任務"+"\t"+Thread.currentThread().getName());
return "abcd";
},executorService).thenRunAsync(() ->{
try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("2号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("3号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
System.out.println("4号任務"+"\t"+Thread.currentThread().getName());
});
System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));
executorService.shutdown();
System.out.println("===============");
}
//e.模拟任務執行速度快的情況,系統底層會使用main線程處理任務
private static void test0() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() ->{
System.out.println("1号任務"+"\t"+Thread.currentThread().getName());
return "abcd";
}).thenRun(() ->{
System.out.println("2号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
System.out.println("3号任務"+"\t"+Thread.currentThread().getName());
}).thenRun(() -> {
System.out.println("4号任務"+"\t"+Thread.currentThread().getName());
});
System.out.println(completableFuture.get(2L,TimeUnit.SECONDS));
System.out.println("===============");
}
}
線程池運作選擇總結
1)沒有傳入自定義線程池時,都使用預設線程池ForkJoinPool;
2)執行第一個任務時傳入了一個自定義線程池 則當使用thenRun執行之後的任務時,都共用同一個自定義線程池;
3)執行第一個任務時傳入了一個自定義線程池 則當使用thenRunAsync執行之後的任務時,隻有第一個任務使用的自定義線程池,後續任務都使用的是預設ForkJoin線程池;
4)有可能處理太快的時候,由于系統底層優化原則,直接利用main線程處理任務。
5)其它如thenAccept、thenAcceptAsync、thenApply和thenApplyAsync等,它們之間的差別也同理。
05、應用場景
先A後B的場景應用
package com.gyd;
import java.util.Random;
import java.util.concurrent.*;
public class CompletableFutureDemo2 {
//先A後B的場景應用
public static void main(String[] args) throws ExecutionException, InterruptedException {
//推薦配置自定義的線程池!!
ExecutorService executorService = Executors.newFixedThreadPool(3);
try {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
//第一步
int result = new Random().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 出結果:" + result);
return result;
},executorService).whenComplete((v, e) -> {
//第二步,擷取第一步的結果
if (null == e) {
System.out.println(Thread.currentThread().getName() + " 接收到結果:" + v);
}
}).exceptionally(e -> {
e.printStackTrace();
System.out.println("發生異常了:" + e);
return null;
});
System.out.println(Thread.currentThread().getName()+"主線程去忙别的事情");
//主線程不要立刻結束,否則CompletableFuture預設使用的線程池會立刻關閉,這裡暫停3秒鐘
try{ Thread.sleep(3000);}catch (Exception e){e.printStackTrace();}
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
并發執行并擷取彙總結果的場景應用
package com.gyd;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class CompletableFutureDemo3 {
static List<NetMall> list = Arrays.asList(
new NetMall("jd"),
new NetMall("dangdang"),
new NetMall("taobao"));
//串行版本
public static List<String> getPrice(List<NetMall> list,String productName) {
return list.stream()
.map(netMall ->
String.format(productName+" in %s price is %.2f",
netMall.getNetMallName(),
netMall.calPrice(productName)))
.collect(Collectors.toList());
}
//并行版本
public static List<String> getPriceByCompletableFuture(List<NetMall> list,String productName) {
return list.stream().map(netMall -> CompletableFuture.supplyAsync(() ->
String.format(productName+"in %s price is %.2f",netMall.getNetMallName(),netMall.calPrice(productName))))
.collect(Collectors.toList())
.stream()
.map(s -> s.join())
.collect(Collectors.toList());
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
long startTime = System.currentTimeMillis();
List<String> list1 = getPrice(list,"mysql");
for (String element : list1) {
System.out.println(element);
}
long endTime = System.currentTimeMillis()-startTime;
System.out.println("costTime:"+endTime);
System.out.println("=======================");
startTime = System.currentTimeMillis();
List<String> list2 = getPriceByCompletableFuture(list,"mysql");
for (String element : list2) {
System.out.println(element);
}
endTime = System.currentTimeMillis()-startTime;
System.out.println("======costTime2 "+endTime);
}
}
class NetMall {
String netMallName;
public String getNetMallName() {
return netMallName;
}
public NetMall(String netMallName){this.netMallName = netMallName;}
public Double calPrice(String productName){
try {
TimeUnit.SECONDS.sleep(1);
}catch (Exception e) {
e.printStackTrace();
}
return ThreadLocalRandom.current().nextDouble()*2+productName.charAt(0);
}
}