java通過Callable和Future來接收線程池的執(zhí)行結(jié)果
在Java的線程執(zhí)行中,不管是直接繼承Thread的方式,還是實(shí)現(xiàn)Runnable接口的方式,都不會(huì)獲取到線程執(zhí)行的返回結(jié)果。這樣如果線程在執(zhí)行過程中出現(xiàn)了錯(cuò)誤,那么主線程也不會(huì)感知到。即使打印了日志,也不能立即拋出異常。事后查看日志才能發(fā)現(xiàn)出現(xiàn)了bug。而且到那時(shí)發(fā)生問題的代碼點(diǎn)距離真正的問題點(diǎn)可能會(huì)相差很遠(yuǎn)。如果在線程池執(zhí)行的過程中出現(xiàn)了bug能及時(shí)地拋出異常,那么這將會(huì)是一個(gè)很好的實(shí)現(xiàn)。解決上述問題的辦法是使用Callable接口,其可以獲取到線程的返回結(jié)果,通過Future的get方法來承接。以下通過一個(gè)1000個(gè)線程實(shí)現(xiàn)累加的例子,來演示Callable和Future的使用:
package com.hys.test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.util.concurrent.ThreadFactoryBuilder; public class Test { private static AtomicInteger num = new AtomicInteger(); public static void main(String[] args) throws InterruptedException, ExecutionException { CountDownLatch latch = new CountDownLatch(1000); ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("increment-pool-%d").build(); ExecutorService poolexecutor = new ThreadPoolExecutor(1000, 1000, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); Future<String> submit = null; for (int i = 0; i < 1000; i++) { if (submit != null && submit.get() != null) { latch.countDown(); continue; } submit = poolexecutor.submit(() -> { try { //這里模擬一個(gè)耗時(shí)很長的操作 num.getAndIncrement(); //int a = 1 / 0; Thread.sleep(1); return null; } catch (Exception e) { return e.toString(); } finally { latch.countDown(); } }); } poolexecutor.shutdown(); //主線程等待所有分線程執(zhí)行完畢后再執(zhí)行 latch.await(); String errorMsg = submit.get(); //如果子線程在執(zhí)行過程中有錯(cuò)誤,則在此拋出該異常 if (errorMsg != null) { throw new RuntimeException(errorMsg); } System.out.println(num); } }
如果每個(gè)線程在執(zhí)行的過程中沒出現(xiàn)問題,則返回的結(jié)果為null。如果返回結(jié)果不為null,則代表該線程執(zhí)行的代碼有問題,此時(shí)將錯(cuò)誤信息返回。放開上述第33行代碼的注釋,以此來模擬一個(gè)算術(shù)異常,再次執(zhí)行上述代碼,可以得到如下的結(jié)果:
Exception in thread "main" java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
at com.hys.test.Test.main(Test.java:49)
由上可以看到,在主線程拋出了算術(shù)異常,可以被感知到。
但是需要注意的一點(diǎn)的是,如果線程的執(zhí)行結(jié)果互相依賴的話,也就是各線程都會(huì)調(diào)用Future的get方法的話,get方法不得不等待任務(wù)執(zhí)行完成,換言之,如果多個(gè)任務(wù)提交后,返回的多個(gè)Future逐一調(diào)用get方法時(shí),將會(huì)依次阻塞,任務(wù)的執(zhí)行從并行變?yōu)榇小H绻虢鉀Q該問題,可以考慮使用Java 8中的CompletableFuture來實(shí)現(xiàn)。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解java之redis篇(spring-data-redis整合)
本篇文章主要介紹了java之redis篇,主要詳細(xì)的介紹了spring-data-redis整合,有興趣的可以了解一下。2017-01-01IDEA遠(yuǎn)程管理docker鏡像及容器服務(wù)的實(shí)現(xiàn)
本文主要介紹了IDEA遠(yuǎn)程管理docker鏡像及容器服務(wù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04詳解SpringBoot中的統(tǒng)一功能處理的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何實(shí)現(xiàn)統(tǒng)一功能處理,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定借鑒價(jià)值,需要的可以參考一下2023-01-01java使用FFmpeg提取音頻的實(shí)現(xiàn)示例
在Java開發(fā)中,我們經(jīng)常會(huì)遇到需要使用FFmpeg來處理音視頻文件的情況,本文主要介紹了java使用FFmpeg提取音頻的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過程
這篇文章主要給大家介紹了關(guān)于springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過程,Spring Boot應(yīng)用中可以配置多個(gè)數(shù)據(jù)源,并根據(jù)注解靈活指定當(dāng)前使用的數(shù)據(jù)源,需要的朋友可以參考下2023-08-08java代碼抓取網(wǎng)頁郵箱的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猨ava代碼抓取網(wǎng)頁郵箱的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06