天天看點

JAVA技術-異步程式設計神器

個人網站:www.gydblog.com

01、簡介

FutureTask的get()方法在Future計算完成之前會一直處于阻塞狀态下,isDone()方法容易耗費CPU資源,對于真正的異步處理我們是希望能通過傳入回調函數,在Future結束時自動調用該回調函數,這樣,我們就不用等待結果。阻塞的方式和異步程式設計的設計理念相違背,而輪詢的方式也會耗費CPU資源。是以JDK8中出現了一種新的工具類:CompletableFuture。

CompletableFuture是FutureTask的增強版,提供的是一種類似觀察者模式的機制,可以讓任務執行完成後通知監聽的一方。在任務執行完成之前,監聽方可以去幹别的事情。

在Java8中,CompletableFuture提供了非常強大的Future的擴充功能,可以幫助我們簡化異步程式設計的複雜性,并且提供了函數式程式設計的能力,可以通過回調的方式處理計算結果,也提供了轉換群組合CompletableFuture的方法。它能代表一個明确完成的Future,也有可能代表一個完成階段(CompletableStage)。它支援在計算完成後觸發一些函數或者執行某些動作。它實作了Future和CompletionStage接口。

JAVA技術-異步程式設計神器

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);

}

}

繼續閱讀