Java 多线程和并发应用程序

小夏 科技 更新 2024-02-09

基本概念。

进程:运行在操作系统上的程序,如QQ和IDE。

线程:一个进程可以有多个线程,例如同时收听声音、图像和弹幕。

多线程:多个线程并发执行。

同步:通过人工控制和调度,保证对共享资源的多线程访问是线程安全的,以保证结果的准确性,如同步关键字,在保证结果准确性的同时提高性能,线程安全优先级高于性能。

并行性:多个 CPU 实例或多台机器同时执行一段处理逻辑。

并发性:通过CPU调度算法,用户看似同时执行,但实际上从CPU操作层面来看,其实并不是同时执行的。

线程的生命周期。

新状态:当程序使用 new 关键字创建线程时,该线程处于新状态,只有 JVM 为其分配内存并初始化其成员变量的值。

就绪状态:当线程对象调用 start() 方法时,线程处于就绪状态。 j**a 虚拟机创建一个方法调用堆栈和程序计数器,以便计划运行。

运行状态:如果处于就绪状态的线程获取 CPU 并开始执行 run() 方法,则该线程处于运行状态。

阻塞状态:当处于运行状态的线程失去占用的资源时,进入阻塞状态。

死态:线程在执行 run() 方法后进入死态。 此外,如果线程执行 interrupt() 或 stop() 方法,那么它也会进入死状态并异常退出。

线程池。 什么是线程池?

一个可以容纳多个线程的容器,其中线程可以重复使用,无需频繁创建线程对象,重复创建线程会消耗过多的系统资源。

为什么要使用线程池?

创建线程的方法有两种,一种是继承线程类,另一种是实现 runnable 接口,实际上就是 runnable 接口。 但是,这两个线程在运行结束时都会被 JVM 销毁,如果线程很多,频繁创建和销毁线程会浪费内存。 那么有没有办法让线程在运行完后不立即被销毁,而是让线程复用并继续执行其他任务呢?

这就是线程池的由来,是线程复用的好办法,避免了重复开销。

线程池任务执行过程。

线程池的优点。

1.线程是稀缺资源,使用线程池可以减少线程的创建和销毁次数,并且每个工作线程都可以重复使用。

2、可根据系统容差调整线程池中的工作线程数,防止服务器因内存消耗过大而崩溃。

线程池参数。

拒绝策略预定义有四种类型:

1)AbortPolicy(默认策略):当线程池的任务队列已满,线程池内所有线程都忙时,会直接拒绝新提交的任务,并抛出rejectedexecution异常。

2)CallerRunsPolicy:当线程池的任务队列已满且线程池中的线程繁忙时,新提交的任务将由提交任务的线程所在的线程池执行。也就是说,当前线程将直接执行任务。

3)DiscardOldestPolicy:当线程池的任务队列已满且线程池中的线程繁忙时,将丢弃队列中最早的任务,并尝试将新提交的任务添加到队列中。

4)DiscardPolicy:当线程池的任务队列已满且线程池中的线程繁忙时,不进行任何处理即可丢弃新提交的任务。

future

运行线程时可以有两种形式,一种具有返回值,另一种没有返回值。 当我们得到一个正在运行的线程的返回值时,我们需要将其与 future 结合使用。

您可以使用 futures 将任务提交到线程池执行,并在需要时获取任务的执行结果。 其主要目的是允许主线程在提交异步任务后继续执行其他操作,而无需等待任务完成。 它可以提高系统的并发性和响应能力。 但是,获得结果很容易导致堵塞。

以下是未来接口的一些常用方法:

1) Boolean cancel(Boolean Mayinterruptifrunning):取消任务的执行。如果正在执行任务,并且 MayInterruptifRunning 参数设置为 true,则尝试中断任务的执行。

2) boolean iscancelled():检查任务是否已取消。

3) boolean isdone():判断任务是否已经完成。

4) v get() 抛出 interruptedexception, executionexception: 获取任务的执行结果。如果任务尚未执行,则 get() 方法将阻塞,直到任务执行完毕。 如果在任务执行过程中发生异常,get() 方法会抛出 executionexception,您可以使用 getcause() 方法获取具体的异常信息。

4) V get(long timeout, timeunit unit) 抛出 interruptedException, executionException, TimeOutException: 获取任务在指定时间内的执行结果,如果任务未在指定时间内执行,则抛出 timeoutException 异常。

ExecutorService 的 submit 方法。

ExecutorService 是 J**A 提供的用于管理线程池的接口。 它可用于执行异步任务和管理线程的生命周期。 submit 方法是由 ExecutorService 接口定义的方法,它将任务提交到线程池并获取将来的对象来表示任务的执行结果。

具体来说,submit 方法用于将可调用或可运行的对象提交到线程池进行执行。 可调用对象是有结果的任务,而可运行对象是没有结果的任务。 submit 方法将任务提交到线程池,并立即返回一个 future 对象,通过该对象可以获取任务执行的结果或取消任务的执行。

您可以通过 submit 方法轻松管理线程池中的任务,也可以使用 future 对象获取任务的状态和结果,也可以取消任务的执行。 通常,我们可以使用 submit 方法而不是 execute 方法,因为它具有更多的功能和灵活性。

submit 和 execute 之间的区别。

submit() 和 execute() 是 ExecutorService 接口用于将任务提交到线程池的两种方法,它们有以下区别:

1)返回值类型不同:submit()方法返回一个未来对象,可用于获取任务的执行结果或取消任务的执行;另一方面,execute() 方法不返回值,也无法获取任务执行的结果。

2)不同的异常处理:submit()方法可以捕获任务执行过程中抛出的异常,将异常封装到以后的对象中,通过调用get()方法或get(long, timeunit)方法获取任务的执行结果,如果任务抛出异常,可以通过executionexception获取异常信息;execute() 方法无法处理任务引发的异常,如果任务引发异常,线程池会将异常记录到控制台。

3)不同的参数类型:submit()方法接受可调用或可运行的对象作为参数,可调用是有返回值的任务,runnable是没有返回值的任务;execute() 方法只接受可运行对象作为参数。 因此,如果你需要获取任务执行的结果,你应该使用 submit() 方法,如果你不需要获取结果,你可以使用 execute() 方法。

一般来说,submit() 方法比 execute() 方法更灵活,可以获取任务的执行结果并捕获任务中抛出的异常,因此在处理需要获取结果或处理异常的任务时,建议使用 submit() 方法。 对于不需要结果的简单任务,可以使用 execute() 方法。

completablefuture

未来使用阻塞来获取结果与异步编程的设计理念背道而驰,轮询方式会消耗不必要的 CPU 资源,因此 jdk8 设计了 completablefuture。

CompletableFuture保留了未来的优点,弥补了其不足。 也就是说,当异步任务完成并且您需要继续操作其结果时,您无需等待。 可以直接使用 thenaccept、thenapply、thencompose 等方式,将之前异步处理的结果交给另一个异步事件处理线程进行处理。

CompletableFuture 对 get 方法的调用也将被阻止以获取结果。

1. 创建异步任务。

创造一个可完成的未来

CompleTableFuture 提供了四种静态方法来创建异步操作。

未指定执行程序的方法使用 forkjoinpoolcommonpool() 作为其线程池异步执行。

如果指定了线程池,则使用指定的线程池。

runasync 方法不支持返回值。

SupplyAsync 可以支持返回值。

任务是异步的。

thenrun/thenrunasync

可完成的未来thenrun/thenrunasync通俗地说,方法是完成第一个任务,然后做第二个任务。 也就是说,一个任务执行完成后,执行**方法; 但是,第一个和第二个任务没有参数传递,第二个任务也没有返回值。

thenrun 和 thenrunasync 有什么区别?

如果在执行第一个任务时没有传入自定义线程池,则 thenrun thenrunasync 自然会使用默认的内置 forkjoin 线程池,并且可能都使用相同的线程。

相反,如果执行第一个任务,则传入自定义线程池:

当调用 thenrun 方法执行第二个任务时,第二个任务和第一个任务共享同一个线程池,因此可以使用相同的线程。

调用 thenrunasync 执行第二个任务时,第一个任务使用您自己的线程池,第二个任务使用分叉连接线程池。

注意:thenaccept 和 thenacceptasync、thenapply 和 thenapplyasync 等的区别与 thenrun 和 thenrunasync 相同。

thenaccept/thenacceptasync

completablefuture 的 thenaccept ThenAcceptAsync 方法表示,第一个任务执行完毕后,执行第二个方法任务,但第一个任务的执行结果会作为输入参数传递给该方法,但该方法没有返回值。

thenapply/thenapplyasync

completablefuture 的 thenapply thenapplyasync 方法表示,第一个任务执行完毕后,执行第二个方法任务,但第一个任务的执行结果会作为输入参数传递给该方法,并且该方法有一个返回值。

exceptionally

completablefuture 的 exceptionally 方法表示当任务执行异常时,执行 ** 处理方法,抛出异常作为参数传递给 ** 方法,并且 ** 方法具有返回值。

whencomplete

completablefuture 的 whencomplete 方法表示,在执行一个任务后,执行了 ** 方法,并将任务结果用作 ** 方法的输入参数,** 方法没有返回值,whencomplete 方法返回的 completablefuture 的结果是第一个任务的结果。

handle

completablefuture 的 handle 方法表示执行一个任务后,执行 ** 方法,并将任务结果用作 ** 方法的输入参数,** 方法有一个返回值,handle 方法返回的 completablefuture 结果是 ** 方法的执行结果。

handle 和 thenapply 之间的区别。

thenapply:当任务发生异常时不,它不会转到然后申请

handle:任务异常是的输入句柄以处理异常。

3.多任务组合**。

和投资组合关系。

ThenCombine ThenAcceptBoth RunAfterBorne 表示:当任务 1 和任务 2 都完成时,将执行任务 3。

不同之处在于 runafterboth 不使用结果作为方法参数,也不返回值。

ThenAcceptBoth:将两个任务的执行结果作为方法参数传递给指定的方法,并且没有返回值。

thencombine:将两个任务的执行结果作为方法参数传递给指定的方法,并有一个返回值。

或组合关系。

applytoeither accepteither runafter两者都表示:两个任务,只要完成一个任务,就会执行第三个任务

区别在于:runaftereither:执行结果不作为方法参数使用,没有返回值。

accepteither:已完成的任务将作为方法输入参数传递给指定的方法,不带返回值。

applytoeither:完成的任务将作为带有返回值的方法输入参数传递给指定的方法。

多任务组合。

「allof」:等待所有任务完成并执行新任务。

「anyof」:完成一项任务后,将执行一项新任务。

4.completablefuture的使用注意事项。

1)future需要获取返回值来获取异常信息。

future 需要获取返回值来获取异常信息。 如果不添加 get() join() 方法,请考虑是否添加 try...。catch...或者使用 exceptionally 方法。

2) completablefuture 的 get() 方法被阻止。

completablefuture 的 get() 方法是阻塞的,如果用它来获取异步调用的返回值,则需要添加超时。

3)线程池的注意事项。

可以在 completablefuture 中使用默认线程池。 当大量请求传入时,如果处理逻辑复杂,响应速度会很慢。 一般情况下,建议您使用自定义线程池来优化线程池配置参数。

future 与 completablefuture。

未来:我们的目标是获得异步任务的结果,但对于未来,我们只能通过使用 get 方法或无限循环来判断是否完成。 异常情况更难处理。

completablefuture:只要我们设置了 ** 函数,就可以实现:

1.任务一完成,我们设置的功能就被执行了(无需考虑任务何时完成)。

2.如果发生异常,将执行处理异常的相同函数,甚至是默认返回值(更耗费人力)。

如果你有复杂的任务,比如依赖问题、组合问题等,也可以写一个处理函数来处理它(可以处理复杂的任务)。

当前 ** 问题。

1. 使用 Spring 线程池,统一管理线程。

如果接口频繁访问,则每次访问都会创建一个新的线程池,这可能会导致访问时间过长和内存耗尽。

如果不调用 shutdown() 方法关闭线程池,可能会导致资源泄漏,或者程序可能无法正常退出。

Spring 线程池。

使用 thenaccept of completablefuture 可以避免阻塞。

在这种情况下,主线程不需要获取返回值,先完成将附件保存到服务器的任务,然后将第一个任务的“文件路径”作为第二个任务的输入参数存储在 fjxx 表中。

completablefuture 使用自定义线程池。

设置超时时间。

invokeall() 方法的超时时间与每个任务的执行时间没有直接关系,因此在使用 invokeall() 方法时,需要根据任务数量和执行时间合理设置超时时间,以避免出现超时时间过长或过短的情况, 这会导致程序性能下降或任务无法完成。

调用未来后get() 方法获取任务执行结果时,还可以设置超时时间和时间单位,防止整个程序因任务执行时间过长而做出响应。

冗余逻辑。

2月** 动态激励计划

相似文章

    JAVA 多线程模型

    局部变量 在堆栈中。局部变量永远不会有安全问题。因为局部变量不是共享的。一个线程,一个堆栈。局部变量 在堆栈中。因此,局部变量永远不会被共享。实例变量 在堆中。实例变量在堆中,并且只有一个堆。静态变量 在方法区域中。静态变量位于方法区域中,并且只有一个方法区域。堆和方法区域由多个线程共享,因此可能存...

    揭秘Java多线程编程的核心技术:从基础到高级的综合分析

    随着多核处理器的日益普及,多线程编程已成为当今应用程序开发的一项基本技能。J A 语言为多线程编程提供了一组丰富的工具和 API,使开发人员能够轻松创建和管理多个线程。本文将对J A多线程编程的核心技术及其实际应用进行深入分析。 J A多线程编程核心技术分析。.线程创建 在 J A 中,可以通过继承...

    FFmpeg CLI 支持多线程,以实现几十年来“最复杂的重构”

    ffmpeg CLI 最近得到了一个重大改进 合并多线程转码流水线 多线程转码管道 FFMPEG .根据开发团队的说法,这项工作是 几十年来 FFMPEG CLI 最复杂的重构之一 主要功能包括添加线程感知转码调度基础设施 将编码任务移动到单独的线程以及各种其他底层更改。这意味着 ffmpeg 现在...

    跟踪在多线程异步系统中传递

    j a 线程异步的常见实现有 new thread executorservice 当然,还有其他的,例如 fork join下面会提到这些,下面主要针对这两个场景结合ddtrace和springboot练习。. .. com.datadoghq dd trace api io.opentracin...

    创建一个新的 Java 项目,向世界问好!

    要开始编写您的第一个 J A 程序,首先需要安装一个开发工具,它是一个集成开发环境 IDE IDE 可以帮助您编写 编译 调试和运行 J A 使您的开发过程更加方便和高效。有很多不同的IDE可供选择,例如Eclipse,NetBeans,IntelliJ Idea等,您可以根据自己的喜好和需求选择一...