热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

completablejava_Java8CompletableFuture教程

Java8有大量的新特性和增强如Lambda表达式,Streams,CompletableFuture等。在本篇文章中我将详细解释清楚Completab

Java 8 有大量的新特性和增强如 Lambda 表达式,Streams,CompletableFuture等。在本篇文章中我将详细解释清楚CompletableFuture以及它所有方法的使用。

什么是CompletableFuture?

在Java中CompletableFuture用于异步编程,异步编程是编写非阻塞的代码,运行的任务在一个单独的线程,与主线程隔离,并且会通知主线程它的进度,成功或者失败。

在这种方式中,主线程不会被阻塞,不需要一直等到子线程完成。主线程可以并行的执行其他任务。

使用这种并行方式,可以极大的提高程序的性能。

Future vs CompletableFuture

CompletableFuture 是 Future API的扩展。

Future 被用于作为一个异步计算结果的引用。提供一个 isDone() 方法来检查计算任务是否完成。当任务完成时,get() 方法用来接收计算任务的结果。

从 Callbale和 Future 教程可以学习更多关于 Future 知识.

Future API 是非常好的 Java 异步编程进阶,但是它缺乏一些非常重要和有用的特性。

Future 的局限性

不能手动完成

当你写了一个函数,用于通过一个远程API获取一个电子商务产品最新价格。因为这个 API 太耗时,你把它允许在一个独立的线程中,并且从你的函数中返回一个 Future。现在假设这个API服务宕机了,这时你想通过该产品的最新缓存价格手工完成这个Future 。你会发现无法这样做。

Future 的结果在非阻塞的情况下,不能执行更进一步的操作

Future 不会通知你它已经完成了,它提供了一个阻塞的 get() 方法通知你结果。你无法给 Future 植入一个回调函数,当 Future 结果可用的时候,用该回调函数自动的调用 Future 的结果。

多个 Future 不能串联在一起组成链式调用

有时候你需要执行一个长时间运行的计算任务,并且当计算任务完成的时候,你需要把它的计算结果发送给另外一个长时间运行的计算任务等等。你会发现你无法使用 Future 创建这样的一个工作流。

不能组合多个 Future 的结果

假设你有10个不同的Future,你想并行的运行,然后在它们运行未完成后运行一些函数。你会发现你也无法使用 Future 这样做。

没有异常处理

Future API 没有任务的异常处理结构居然有如此多的限制,幸好我们有CompletableFuture,你可以使用 CompletableFuture 达到以上所有目的。

CompletableFuture 实现了 Future 和 CompletionStage接口,并且提供了许多关于创建,链式调用和组合多个 Future 的便利方法集,而且有广泛的异常处理支持。

创建 CompletableFuture

1. 简单的例子

可以使用如下无参构造函数简单的创建 CompletableFuture:

CompletableFuture completableFuture = new CompletableFuture();

这是一个最简单的 CompletableFuture,想获取CompletableFuture 的结果可以使用 CompletableFuture.get() 方法:

String result = completableFuture.get()

get() 方法会一直阻塞直到 Future 完成。因此,以上的调用将被永远阻塞,因为该Future一直不会完成。

你可以使用 CompletableFuture.complete() 手工的完成一个 Future:

completableFuture.complete("Future's Result")

所有等待这个 Future 的客户端都将得到一个指定的结果,并且 completableFuture.complete() 之后的调用将被忽略。

2. 使用 runAsync() 运行异步计算

如果你想异步的运行一个后台任务并且不想改任务返回任务东西,这时候可以使用 CompletableFuture.runAsync()方法,它持有一个Runnable对象,并返回 CompletableFuture。

// Run a task specified by a Runnable Object asynchronously.

CompletableFuture future = CompletableFuture.runAsync(new Runnable() {

@Override

public void run() {

// Simulate a long-running Job

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

System.out.println("I'll run in a separate thread than the main thread.");

}

});

// Block and wait for the future to complete

future.get()

你也可以以 lambda 表达式的形式传入 Runnable 对象:

// Using Lambda Expression

CompletableFuture future = CompletableFuture.runAsync(() -> {

// Simulate a long-running Job

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

System.out.println("I'll run in a separate thread than the main thread.");

});

在本文中,我使用lambda表达式会比较频繁,如果以前你没有使用过,建议你也多使用lambda 表达式。

3. 使用 supplyAsync() 运行一个异步任务并且返回结果

当任务不需要返回任何东西的时候, CompletableFuture.runAsync() 非常有用。但是如果你的后台任务需要返回一些结果应该要怎么样?

CompletableFuture.supplyAsync() 就是你的选择。它持有supplier 并且返回CompletableFuture,T 是通过调用 传入的supplier取得的值的类型。

// Run a task specified by a Supplier object asynchronously

CompletableFuture future = CompletableFuture.supplyAsync(new Supplier() {

@Override

public String get() {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Result of the asynchronous computation";

}

});

// Block and get the result of the Future

String result = future.get();

System.out.println(result);

Supplier 是一个简单的函数式接口,表示supplier的结果。它有一个get()方法,该方法可以写入你的后台任务中,并且返回结果。

你可以使用lambda表达式使得上面的示例更加简明:

// Using Lambda Expression

CompletableFuture future = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Result of the asynchronous computation";

});

一个关于Executor 和Thread Pool笔记

你可能想知道,我们知道runAsync()和supplyAsync()方法在单独的线程中执行他们的任务。但是我们不会永远只创建一个线程。

CompletableFuture可以从全局的 ForkJoinPool.commonPool()获得一个线程中执行这些任务。

但是你也可以创建一个线程池并传给runAsync()和supplyAsync()方法来让他们从线程池中获取一个线程执行它们的任务。

CompletableFuture API 的所有方法都有两个变体-一个接受Executor作为参数,另一个不这样:

// Variations of runAsync() and supplyAsync() methods

static CompletableFuture runAsync(Runnable runnable)

static CompletableFuture runAsync(Runnable runnable, Executor executor)

static CompletableFuture supplyAsync(Supplier supplier)

static CompletableFuture supplyAsync(Supplier supplier, Executor executor)

创建一个线程池,并传递给其中一个方法:

Executor executor = Executors.newFixedThreadPool(10);

CompletableFuture future = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Result of the asynchronous computation";

}, executor);

在 CompletableFuture 转换和运行

CompletableFuture.get()方法是阻塞的。它会一直等到Future完成并且在完成后返回结果。

但是,这是我们想要的吗?对于构建异步系统,我们应该附上一个回调给CompletableFuture,当Future完成的时候,自动的获取结果。

如果我们不想等待结果返回,我们可以把需要等待Future完成执行的逻辑写入到回调函数中。

可以使用 thenApply(), thenAccept() 和thenRun()方法附上一个回调给CompletableFuture。

1. thenApply()

可以使用 thenApply() 处理和改变CompletableFuture的结果。持有一个Function作为参数。Function是一个简单的函数式接口,接受一个T类型的参数,产出一个R类型的结果。

// Create a CompletableFuture

CompletableFuture whatsYourNameFuture = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Rajeev";

});

// Attach a callback to the Future using thenApply()

CompletableFuture greetingFuture = whatsYourNameFuture.thenApply(name -> {

return "Hello " + name;

});

// Block and get the result of the future.

System.out.println(greetingFuture.get()); // Hello Rajeev

你也可以通过附加一系列的thenApply()在回调方法 在CompletableFuture写一个连续的转换。这样的话,结果中的一个 thenApply方法就会传递给该系列的另外一个 thenApply方法。

CompletableFuture welcomeText = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Rajeev";

}).thenApply(name -> {

return "Hello " + name;

}).thenApply(greeting -> {

return greeting + ", Welcome to the CalliCoder Blog";

});

System.out.println(welcomeText.get());

// Prints - Hello Rajeev, Welcome to the CalliCoder Blog

2. thenAccept() 和 thenRun()

如果你不想从你的回调函数中返回任何东西,仅仅想在Future完成后运行一些代码片段,你可以使用thenAccept()和 thenRun()方法,这些方法经常在调用链的最末端的最后一个回调函数中使用。

CompletableFuture.thenAccept()持有一个Consumer,返回一个CompletableFuture。它可以访问CompletableFuture的结果:

// thenAccept() example

CompletableFuture.supplyAsync(() -> {

return ProductService.getProductDetail(productId);

}).thenAccept(product -> {

System.out.println("Got product detail from remote service " + product.getName())

});

虽然thenAccept()可以访问CompletableFuture的结果,但thenRun()不能访Future的结果,它持有一个Runnable返回CompletableFuture:

// thenRun() example

CompletableFuture.supplyAsync(() -> {

// Run some computation

}).thenRun(() -> {

// Computation Finished.

});

异步回调方法的笔记

CompletableFuture提供的所有回调方法都有两个变体:

`// thenApply() variants

CompletableFuture thenApply(Function super T,? extends U> fn)

CompletableFuture thenApplyAsync(Function super T,? extends U> fn)

CompletableFuture thenApplyAsync(Function super T,? extends U> fn, Executor executor)`

这些异步回调变体通过在独立的线程中执行回调任务帮助你进一步执行并行计算。

以下示例:

CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Some Result"

}).thenApply(result -> {

/*

Executed in the same thread where the supplyAsync() task is executed

or in the main thread If the supplyAsync() task completes immediately (Remove sleep() call to verify)

*/

return "Processed Result"

})

在以上示例中,在thenApply()中的任务和在supplyAsync()中的任务执行在相同的线程中。任何supplyAsync()立即执行完成,那就是执行在主线程中(尝试删除sleep测试下)。

为了控制执行回调任务的线程,你可以使用异步回调。如果你使用thenApplyAsync()回调,将从ForkJoinPool.commonPool()获取不同的线程执行。

CompletableFuture.supplyAsync(() -> {

return "Some Result"

}).thenApplyAsync(result -> {

// Executed in a different thread from ForkJoinPool.commonPool()

return "Processed Result"

})

此外,如果你传入一个Executor到thenApplyAsync()回调中,,任务将从Executor线程池获取一个线程执行。

Executor executor = Executors.newFixedThreadPool(2);

CompletableFuture.supplyAsync(() -> {

return "Some result"

}).thenApplyAsync(result -> {

// Executed in a thread obtained from the executor

return "Processed Result"

}, executor);

组合两个CompletableFuture

1. 使用 thenCompose()组合两个独立的future

假设你想从一个远程API中获取一个用户的详细信息,一旦用户信息可用,你想从另外一个服务中获取他的贷方。

考虑下以下两个方法getUserDetail()和getCreditRating()的实现:

CompletableFuture getUsersDetail(String userId) {

return CompletableFuture.supplyAsync(() -> {

UserService.getUserDetails(userId);

});

}

CompletableFuture getCreditRating(User user) {

return CompletableFuture.supplyAsync(() -> {

CreditRatingService.getCreditRating(user);

});

}

现在让我们弄明白当使用了thenApply()后是否会达到我们期望的结果-

CompletableFuture> result = getUserDetail(userId)

.thenApply(user -> getCreditRating(user));

在更早的示例中,Supplier函数传入thenApply将返回一个简单的值,但是在本例中,将返回一个CompletableFuture。以上示例的最终结果是一个嵌套的CompletableFuture。

如果你想获取最终的结果给最顶层future,使用 thenCompose()方法代替-

CompletableFuture result = getUserDetail(userId)

.thenCompose(user -> getCreditRating(user));

因此,规则就是-如果你的回调函数返回一个CompletableFuture,但是你想从CompletableFuture链中获取一个直接合并后的结果,这时候你可以使用thenCompose()。

2. 使用thenCombine()组合两个独立的 future

虽然thenCompose()被用于当一个future依赖另外一个future的时候用来组合两个future。thenCombine()被用来当两个独立的Future都完成的时候,用来做一些事情。

System.out.println("Retrieving weight.");

CompletableFuture weightInKgFuture = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return 65.0;

});

System.out.println("Retrieving height.");

CompletableFuture heightInCmFuture = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return 177.8;

});

System.out.println("Calculating BMI.");

CompletableFuture combinedFuture = weightInKgFuture

.thenCombine(heightInCmFuture, (weightInKg, heightInCm) -> {

Double heightInMeter = heightInCm/100;

return weightInKg/(heightInMeter*heightInMeter);

});

System.out.println("Your BMI is - " + combinedFuture.get());

当两个Future都完成的时候,传给``thenCombine()的回调函数将被调用。

组合多个CompletableFuture

我们使用thenCompose()和 thenCombine()把两个CompletableFuture组合在一起。现在如果你想组合任意数量的CompletableFuture,应该怎么做?我们可以使用以下两个方法组合任意数量的CompletableFuture。

static CompletableFuture allOf(CompletableFuture>... cfs)

static CompletableFuture anyOf(CompletableFuture>... cfs)

1. CompletableFuture.allOf()

CompletableFuture.allOf的使用场景是当你一个列表的独立future,并且你想在它们都完成后并行的做一些事情。

假设你想下载一个网站的100个不同的页面。你可以串行的做这个操作,但是这非常消耗时间。因此你想写一个函数,传入一个页面链接,返回一个CompletableFuture,异步的下载页面内容。

CompletableFuture downloadWebPage(String pageLink) {

return CompletableFuture.supplyAsync(() -> {

// Code to download and return the web page's content

});

}

现在,当所有的页面已经下载完毕,你想计算包含关键字CompletableFuture页面的数量。可以使用CompletableFuture.allOf()达成目的。

List webPageLinks = Arrays.asList(...) // A list of 100 web page links

// Download contents of all the web pages asynchronously

List> pageContentFutures = webPageLinks.stream()

.map(webPageLink -> downloadWebPage(webPageLink))

.collect(Collectors.toList());

// Create a combined Future using allOf()

CompletableFuture allFutures = CompletableFuture.allOf(

pageContentFutures.toArray(new CompletableFuture[pageContentFutures.size()])

);

使用CompletableFuture.allOf()的问题是它返回CompletableFuture。但是我们可以通过写一些额外的代码来获取所有封装的CompletableFuture结果。

// When all the Futures are completed, call `future.join()` to get their results and collect the results in a list -

CompletableFuture> allPageContentsFuture = allFutures.thenApply(v -> {

return pageContentFutures.stream()

.map(pageContentFuture -> pageContentFuture.join())

.collect(Collectors.toList());

});

花一些时间理解下以上代码片段。当所有future完成的时候,我们调用了future.join(),因此我们不会在任何地方阻塞。

join()方法和get()方法非常类似,这唯一不同的地方是如果最顶层的CompletableFuture完成的时候发生了异常,它会抛出一个未经检查的异常。

现在让我们计算包含关键字页面的数量。

// Count the number of web pages having the "CompletableFuture" keyword.

CompletableFuture countFuture = allPageContentsFuture.thenApply(pageContents -> {

return pageContents.stream()

.filter(pageContent -> pageContent.contains("CompletableFuture"))

.count();

});

System.out.println("Number of Web Pages having CompletableFuture keyword - " +

countFuture.get());

2. CompletableFuture.anyOf()

CompletableFuture.anyOf()和其名字介绍的一样,当任何一个CompletableFuture完成的时候【相同的结果类型】,返回一个新的CompletableFuture。以下示例:

CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(2);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Result of Future 1";

});

CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Result of Future 2";

});

CompletableFuture future3 = CompletableFuture.supplyAsync(() -> {

try {

TimeUnit.SECONDS.sleep(3);

} catch (InterruptedException e) {

throw new IllegalStateException(e);

}

return "Result of Future 3";

});

CompletableFuture anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);

System.out.println(anyOfFuture.get()); // Result of Future 2

在以上示例中,当三个中的任何一个CompletableFuture完成, anyOfFuture就会完成。因为future2的休眠时间最少,因此她最先完成,最终的结果将是future2的结果。

CompletableFuture.anyOf()传入一个Future可变参数,返回CompletableFuture。CompletableFuture.anyOf()的问题是如果你的CompletableFuture返回的结果是不同类型的,这时候你讲会不知道你最终CompletableFuture是什么类型。

CompletableFuture 异常处理

我们探寻了怎样创建CompletableFuture,转换它们,并组合多个CompletableFuture。现在让我们弄明白当发生错误的时候我们应该怎么做。

首先让我们明白在一个回调链中错误是怎么传递的。思考下以下回调链:

CompletableFuture.supplyAsync(() -> {

// Code which might throw an exception

return "Some result";

}).thenApply(result -> {

return "processed result";

}).thenApply(result -> {

return "result after further processing";

}).thenAccept(result -> {

// do something with the final result

});

如果在原始的supplyAsync()任务中发生一个错误,这时候没有任何thenApply会被调用并且future将以一个异常结束。如果在第一个thenApply发生错误,这时候第二个和第三个将不会被调用,同样的,future将以异常结束。

1. 使用 exceptionally() 回调处理异常

exceptionally()回调给你一个从原始Future中生成的错误恢复的机会。你可以在这里记录这个异常并返回一个默认值。

Integer age = -1;

CompletableFuture maturityFuture = CompletableFuture.supplyAsync(() -> {

if(age <0) {

throw new IllegalArgumentException("Age can not be negative");

}

if(age > 18) {

return "Adult";

} else {

return "Child";

}

}).exceptionally(ex -> {

System.out.println("Oops! We have an exception - " &#43; ex.getMessage());

return "Unknown!";

});

System.out.println("Maturity : " &#43; maturityFuture.get());

2. 使用 handle() 方法处理异常

API提供了一个更通用的方法 - handle()从异常恢复&#xff0c;无论一个异常是否发生它都会被调用。

Integer age &#61; -1;

CompletableFuture maturityFuture &#61; CompletableFuture.supplyAsync(() -> {

if(age <0) {

throw new IllegalArgumentException("Age can not be negative");

}

if(age > 18) {

return "Adult";

} else {

return "Child";

}

}).handle((res, ex) -> {

if(ex !&#61; null) {

System.out.println("Oops! We have an exception - " &#43; ex.getMessage());

return "Unknown!";

}

return res;

});

System.out.println("Maturity : " &#43; maturityFuture.get());

如果异常发生&#xff0c;res参数将是 null&#xff0c;否则&#xff0c;ex将是 null。



推荐阅读
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • 达人评测 酷睿i5 12450h和锐龙r7 5800h选哪个好 i512450h和r75800h对比
    本文介绍了达人评测酷睿i5 12450h和锐龙r7 5800h选哪个好的相关知识,包括两者的基本配置和重要考虑点。希望对你在选择时提供一定的参考价值。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
author-avatar
dmcm0011
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有