iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >java异步编程的实现方式有哪些
  • 197
分享到

java异步编程的实现方式有哪些

2023-07-05 16:07:27 197人浏览 泡泡鱼
摘要

这篇文章主要介绍“java异步编程的实现方式有哪些”,在日常操作中,相信很多人在java异步编程的实现方式有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java异步编程的实现方式有哪些”的疑惑有所帮助!

这篇文章主要介绍“java异步编程的实现方式有哪些”,在日常操作中,相信很多人在java异步编程的实现方式有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java异步编程的实现方式有哪些”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

同步编程

java异步编程的实现方式有哪些

当用户创建一笔电商交易订单时,要经历的业务逻辑流程还是很长的,每一步都要耗费一定的时间,那么整体的RT就会比较长。

于是,聪明的人们开始思考能不能将一些非核心业务从主流程中剥离出来,于是有了异步编程雏形。

异步编程是让程序并发运行的一种手段。它允许多个事件同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行。

核心思路:采用多线程优化性能,将串行操作变成并行操作。异步模式设计的程序可以显著减少线程等待,从而在高吞吐量场景中,极大提升系统的整体性能,显著降低时延。

接下来,我们来讲下异步有哪些编程实现方式

一、线程 Thread

直接继承 Thread类 是创建异步线程最简单的方式。

首先,创建Thread子类,普通类或匿名内部类方式;然后创建子类实例;最后通过start()方法启动线程。

public class AsyncThread extends Thread{    @Override    public void run() {        System.out.println("当前线程名称:" + this.getName() + ", 执行线程名称:" + Thread.currentThread().getName() + "-hello");    }}
public static void main(String[] args) {   // 模拟业务流程  // .......      // 创建异步线程     AsyncThread asyncThread = new AsyncThread();     // 启动异步线程    asyncThread.start();}

当然如果每次都创建一个 Thread线程,频繁的创建、销毁,浪费系统资源。我们可以采用线程池

@Bean(name = "executorService")public ExecutorService downloadExecutorService() {    return new ThreadPoolExecutor(20, 40, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2000),            new ThreadFactoryBuilder().setNameFORMat("defaultExecutorService-%d").build(),            (r, executor) -> log.error("defaultExecutor pool is full! "));}

将业务逻辑封装到 Runnable 或 Callable 中,交由 线程池 来执行

java异步编程的实现方式有哪些

二、Future

上述方式虽然达到了多线程并行处理,但有些业务不仅仅要执行过程,还要获取执行结果。

Java 从1.5版本开始,提供了 Callable 和 Future,可以在任务执行完毕之后得到任务执行结果。

当然也提供了其他功能,如:取消任务、查询任务是否完成等

Future类位于java.util.concurrent包下,接口定义:

public interface Future<V> {    boolean cancel(boolean mayInterruptIfRunning);    boolean isCancelled();    boolean isDone();    V get() throws InterruptedException, ExecutionException;    V get(long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;}

方法描述:

  • cancel():取消任务,如果取消任务成功返回true,如果取消任务失败则返回false

  • isCancelled():表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true

  • isDone():表示任务是否已经完成,如果完成,返回true

  • get():获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回

  • get(long timeout, TimeUnit unit):用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null

代码示例:

public class CallableAndFuture {     public static ExecutorService executorService = new ThreadPoolExecutor(4, 40,            0L, TimeUnit.MILLISECONDS,            new LinkedBlockingQueue<Runnable>(1024), new ThreadFactoryBuilder()            .setNameFormat("demo-pool-%d").build(), new ThreadPoolExecutor.AbortPolicy());      static class MyCallable implements Callable<String> {        @Override        public String call() throws Exception {            return "异步处理,Callable 返回结果";        }    }     public static void main(String[] args) {        Future<String> future = executorService.submit(new MyCallable());        try {            System.out.println(future.get());        } catch (Exception e) {            // nodo        } finally {            executorService.shutdown();        }    }}

Future 表示一个可能还没有完成的异步任务的结果,通过 get 方法获取执行结果,该方法会阻塞直到任务返回结果。

三、FutureTask

FutureTask 实现了 RunnableFuture 接口,则 RunnableFuture 接口继承了 Runnable 接口和 Future 接口,所以可以将 FutureTask 对象作为任务提交给 ThreadPoolExecutor 去执行,也可以直接被 Thread 执行;又因为实现了 Future 接口,所以也能用来获得任务的执行结果。

FutureTask 构造函数:

public FutureTask(Callable<V> callable)public FutureTask(Runnable runnable, V result)

FutureTask 常用来封装 Callable 和 Runnable,可以作为一个任务提交到线程池中执行。除了作为一个独立的类之外,也提供了一些功能性函数供我们创建自定义 task 类使用。

FutureTask 线程安全由CAS来保证。

ExecutorService executor = Executors.newCachedThreadPool();// FutureTask包装callbale任务,再交给线程池执行FutureTask<Integer> futureTask = new FutureTask<>(() -> {    System.out.println("子线程开始计算:");    Integer sum = 0;    for (int i = 1; i <= 100; i++)        sum += i;    return sum;}); // 线程池执行任务, 运行结果在 futureTask 对象里面executor.submit(futureTask); try {    System.out.println("task运行结果计算的总和为:" + futureTask.get());} catch (Exception e) {    e.printStackTrace();}executor.shutdown();

Callable 和 Future 的区别:Callable 用于产生结果,Future 用于获取结果

如果是对多个任务多次自由串行、或并行组合,涉及多个线程之间同步阻塞获取结果,Future 代码实现会比较繁琐,需要我们手动处理各个交叉点,很容易出错。

四、异步框架 CompletableFuture

Future 类通过 get() 方法阻塞等待获取异步执行的运行结果,性能比较差。

jdk1.8 中,Java 提供了 CompletableFuture 类,它是基于异步函数式编程。相对阻塞式等待返回结果,CompletableFuture 可以通过回调的方式来处理计算结果,实现了异步非阻塞,性能更优。

优点

  • 异步任务结束时,会自动回调某个对象的方法

  • 异步任务出错时,会自动回调某个对象的方法

  • 主线程设置好回调后,不再关心异步任务的执行

泡茶示例:

java异步编程的实现方式有哪些

(内容摘自:极客时间的《Java 并发编程实战》)

//任务1:洗水壶->烧开水CompletableFuture<Void> f1 =        CompletableFuture.runAsync(() -> {            System.out.println("T1:洗水壶...");            sleep(1, TimeUnit.SECONDS);             System.out.println("T1:烧开水...");            sleep(15, TimeUnit.SECONDS);        }); //任务2:洗茶壶->洗茶杯->拿茶叶CompletableFuture<String> f2 =        CompletableFuture.supplyAsync(() -> {            System.out.println("T2:洗茶壶...");            sleep(1, TimeUnit.SECONDS);             System.out.println("T2:洗茶杯...");            sleep(2, TimeUnit.SECONDS);             System.out.println("T2:拿茶叶...");            sleep(1, TimeUnit.SECONDS);            return "龙井";        }); //任务3:任务1和任务2完成后执行:泡茶CompletableFuture<String> f3 =        f1.thenCombine(f2, (__, tf) -> {            System.out.println("T1:拿到茶叶:" + tf);            System.out.println("T1:泡茶...");            return "上茶:" + tf;        }); //等待任务3执行结果System.out.println(f3.join()); }

CompletableFuture 提供了非常丰富的api,大约有50种处理串行,并行,组合以及处理错误的方法。

五、 SpringBoot 注解 @Async

除了硬编码的异步编程处理方式,springBoot 框架还提供了 注解式 解决方案,以 方法体 为边界,方法体内部的代码逻辑全部按异步方式执行。

首先,使用 @EnableAsync 启用异步注解

@SpringBootApplication@EnableAsyncpublic class StartApplication {     public static void main(String[] args) {        SpringApplication.run(StartApplication.class, args);    }}

自定义线程池:

@Configuration@Slf4jpublic class ThreadPoolConfiguration {     @Bean(name = "defaultThreadPoolExecutor", destroyMethod = "shutdown")    public ThreadPoolExecutor systemCheckPoolExecutorService() {         return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS,                new LinkedBlockingQueue<Runnable>(10000),                new ThreadFactoryBuilder().setNameFormat("default-executor-%d").build(),                (r, executor) -> log.error("system pool is full! "));    }}

在异步处理的方法上添加注解 @Async ,当对 execute 方法 调用时,通过自定义的线程池 defaultThreadPoolExecutor 异步化执行  execute 方法

@Servicepublic class AsyncServiceImpl implements AsyncService {     @Async("defaultThreadPoolExecutor")    public Boolean execute(Integer num) {        System.out.println("线程:" + Thread.currentThread().getName() + " , 任务:" + num);        return true;    } }

用 @Async 注解标记的方法,称为异步方法。在Spring Boot应用中使用 @Async 很简单:

  • 调用异步方法类上或者启动类加上注解 @EnableAsync

  • 在需要被异步调用的方法外加上 @Async

  • 所使用的 @Async 注解方法的类对象应该是Spring容器管理的bean对象;

六、Spring ApplicationEvent 事件

事件机制在一些大型项目中被经常使用,Spring 专门提供了一套事件机制的接口,满足了架构原则上的解耦。

ApplicationContext 通过 ApplicationEvent 类和 ApplicationListener 接口进行事件处理。如果将实现 ApplicationListener 接口的 bean 注入到上下文中,则每次使用 ApplicationContext 发布 ApplicationEvent 时,都会通知该 bean。本质上,这是标准的观察者设计模式

ApplicationEvent 是由 Spring 提供的所有 Event 类的基类

首先,自定义业务事件子类,继承自 ApplicationEvent,通过泛型注入业务模型参数类。相当于 MQ 的消息体。

public class OrderEvent extends AbstractGenericEvent<OrderModel> {    public OrderEvent(OrderModel source) {        super(source);    }}

然后,编写事件监听器。ApplicationListener 接口是由 Spring 提供的事件订阅者必须实现的接口,我们需要定义一个子类,继承 ApplicationListener。相当于 MQ 的消费端

@Componentpublic class OrderEventListener implements ApplicationListener<OrderEvent> {    @Override    public void onApplicationEvent(OrderEvent event) {         System.out.println("【OrderEventListener】监听器处理!" + JSON.tojsONString(event.getSource()));     }}

最后,发布事件,把某个事件告诉所有与这个事件相关的监听器。相当于 MQ 的生产端。

OrderModel orderModel = new OrderModel();orderModel.setOrderId((long) i);orderModel.setBuyerName("Tom-" + i);orderModel.setSellerName("judy-" + i);orderModel.setAmount(100L);// 发布Spring事件通知SpringUtils.getApplicationContext().publishEvent(new OrderEvent(orderModel));

加个餐:

[消费端]线程:Http-NIO-8090-exec-1,消费事件 {"amount":100.0,"buyerName":"Tom-1","orderId":1,"sellerName":"judy-1"}
[生产端]线程:http-nio-8090-exec-1,发布事件 1
[消费端]线程:http-nio-8090-exec-1,消费事件 {"amount":100.0,"buyerName":"Tom-2","orderId":2,"sellerName":"judy-2"}
[生产端]线程:http-nio-8090-exec-1,发布事件 2
[消费端]线程:http-nio-8090-exec-1,消费事件 {"amount":100.0,"buyerName":"Tom-3","orderId":3,"sellerName":"judy-3"}
[生产端]线程:http-nio-8090-exec-1,发布事件 3

上面是跑了个demo的运行结果,我们发现无论生产端还是消费端,使用了同一个线程 http-nio-8090-exec-1,Spring 框架的事件机制默认是同步阻塞的。只是在代码规范方面做了解耦,有较好的扩展性,但底层还是采用同步调用方式。

那么问题来了,如果想实现异步调用,如何处理?

我们需要手动创建一个 SimpleApplicationEventMulticaster,并设置 TaskExecutor,此时所有的消费事件采用异步线程执行。

@Componentpublic class SprinGConfiguration {     @Bean    public SimpleApplicationEventMulticaster applicationEventMulticaster(@Qualifier("defaultThreadPoolExecutor") ThreadPoolExecutor defaultThreadPoolExecutor) {        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();        simpleApplicationEventMulticaster.setTaskExecutor(defaultThreadPoolExecutor);        return simpleApplicationEventMulticaster;    } }

我们看下改造后的运行结果:

[生产端]线程:http-nio-8090-exec-1,发布事件 1
[生产端]线程:http-nio-8090-exec-1,发布事件 2
[生产端]线程:http-nio-8090-exec-1,发布事件 3
[消费端]线程:default-executor-1,消费事件 {"amount":100.0,"buyerName":"Tom-2","orderId":2,"sellerName":"judy-2"}
[消费端]线程:default-executor-2,消费事件 {"amount":100.0,"buyerName":"Tom-1","orderId":1,"sellerName":"judy-1"}
[消费端]线程:default-executor-0,消费事件 {"amount":100.0,"buyerName":"Tom-3","orderId":3,"sellerName":"judy-3"}

SimpleApplicationEventMulticaster 这个我们自己实例化的 Bean 与系统默认的加载顺序如何?会不会有冲突?

查了下 Spring 源码,处理逻辑在 AbstractApplicationContext#initApplicationEventMulticaster 方法中,通过 beanFactory 查找是否有自定义的 Bean,如果没有,容器会自己 new 一个 SimpleApplicationEventMulticaster 对象注入到容器中。

java异步编程的实现方式有哪些

七、消息队列

异步架构是互联网系统中一种典型架构模式,与同步架构相对应。而消息队列天生就是这种异步架构,具有超高吞吐量和超低时延。

消息队列异步架构的主要角色包括消息生产者、消息队列和消息消费者。

java异步编程的实现方式有哪些

消息生产者就是主应用程序,生产者将调用请求封装成消息发送给消息队列。

消息队列的职责就是缓冲消息,等待消费者消费。根据消费方式又分为点对点模式发布订阅模式两种。

消息消费者,用来从消息队列中拉取、消费消息,完成业务逻辑处理。

当然市面上消息队列框架非常多,常见的有RabbitMQkafkaRocketMQ、ActiveMQ 和 Pulsar 等

java异步编程的实现方式有哪些

不同的消息队列的功能特性会略有不同,但整体架构类似,这里就不展开了。

我们只需要记住一个关键点,借助消息队列这个中间件可以高效的实现异步编程。

到此,关于“java异步编程的实现方式有哪些”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

--结束END--

本文标题: java异步编程的实现方式有哪些

本文链接: https://www.lsjlt.com/news/353009.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • java异步编程的实现方式有哪些
    这篇文章主要介绍“java异步编程的实现方式有哪些”,在日常操作中,相信很多人在java异步编程的实现方式有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java异步编程的实现方式有哪些”的疑惑有所帮助!...
    99+
    2023-07-05
  • java异步编程有哪些方式
    本篇内容介绍了“java异步编程有哪些方式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!为什么需要异步?操作系统可以看作是个虚拟机(VM),...
    99+
    2023-06-15
  • Java异步编程API的打包方式有哪些?
    Java异步编程是现代软件开发中非常重要的一部分,它可以帮助我们提高程序的性能和响应速度。在Java中,异步编程API有很多种不同的打包方式,本文将为您介绍其中几种常见的打包方式,并附上相应的演示代码。 CompletableFuture...
    99+
    2023-08-28
    异步编程 api 打包
  • Java异步非阻塞编程的方式有哪些
    这篇文章主要讲解了“Java异步非阻塞编程的方式有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java异步非阻塞编程的方式有哪些”吧!1 服务端执行,最简单的同步调用方式:缺陷:服务端...
    99+
    2023-06-20
  • javascript异步编程有哪些方式
    这篇文章主要为大家展示了“javascript异步编程有哪些方式”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“javascript异步编程有哪些方式”这篇文章吧...
    99+
    2024-04-02
  • Java和Bash:缓存的异步编程方式有哪些?
    在软件开发领域中,缓存是一种非常重要的技术。缓存可以提高程序的性能,减少对于后端服务的访问次数,从而缩短响应时间,提高用户体验。但是,缓存的使用也带来了一些问题,比如缓存的一致性、缓存的更新和失效等等。为了解决这些问题,我们需要使用异步编...
    99+
    2023-06-28
    bash 缓存 异步编程
  • web中异步编程的方式有哪些
    本篇内容主要讲解“web中异步编程的方式有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“web中异步编程的方式有哪些”吧!回调函数因为 f1 是一个需要一定...
    99+
    2024-04-02
  • javascript中实现异步编程的方法有哪些
    javascript中实现异步编程的方法有哪些?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。javascript异步编程的4种方法:1、回调函数,这是异步编程最...
    99+
    2023-06-14
  • Java 的异步编程 (5 种异步实现方式详解)
    一、线程异步 创建一个异步线程 public class AsyncThread extends Thread{ @Override public void run() { System.out.println("...
    99+
    2023-09-03
    java jvm 开发语言
  • Java 异步编程 (5 种异步实现方式详解)
    同步操作如果遇到一个耗时的方法,需要阻塞等待,那么我们有没有办法解决呢?让它异步执行,下面我会详解异步及实现 @mikechen 目录 什么是异步?一、线程异步二、Future异步三、CompletableFuture异步四、Spring...
    99+
    2023-09-02
    java jvm 数据库
  • java实现异步的方法有哪些
    Java实现异步的方法有以下几种:1. 使用线程池:可以通过ExecutorService接口创建线程池,然后使用submit()或...
    99+
    2023-08-09
    java
  • 异步编程中索引重定向的实现方式有哪些?
    异步编程是现代编程语言中的热门话题,它可以提高应用程序的性能和响应能力。但是,异步编程也带来了一些问题,其中之一就是索引重定向。当我们在执行异步操作时,可能会需要对一个索引进行操作,但是由于异步操作的特性,索引可能会在操作完成前发生变化,...
    99+
    2023-11-13
    索引 重定向 异步编程
  • 文件操作:PHP和NumPy的异步编程实现方式有哪些?
    在当今的计算机科学领域中,异步编程已经成为一个非常重要的话题。异步编程是一种编程方式,它允许程序在等待某些操作完成的同时,可以继续执行其他任务。这种编程方式可以提高程序的并发性和响应速度,特别是在文件操作等I/O密集型任务中。在本文中,我...
    99+
    2023-10-05
    numpy 异步编程 文件
  • java异步编程的7种实现方式小结
    目录同步编程一、线程 Thread二、Future三、FutureTask四、异步框架 CompletableFuture五、 SpringBoot 注解 @Async六、Sprin...
    99+
    2023-03-23
    java异步编程 Java异步
  • JS异步编程有哪些方案
    这篇文章主要讲解了“JS异步编程有哪些方案”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JS异步编程有哪些方案”吧!关于 JS 单线程、EventLoop ...
    99+
    2024-04-02
  • JS异步编程方案有哪些
    本篇内容主要讲解“JS异步编程方案有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JS异步编程方案有哪些”吧!一、同步与异步我们可以通俗理解为异步就是一个任...
    99+
    2024-04-02
  • java异步调用的实现方法有哪些
    在Java中,有以下几种方式可以实现异步调用:1. 使用Thread类:可以通过创建和启动一个新的线程来实现异步调用。这种方式比较底...
    99+
    2023-08-12
    java
  • C#BeginInvoke实现异步编程方式
    目录1.等待模式2.轮询模式3.回调模式总结BeginInvoke实现异步编程的三种模式: 1.等待模式 在发起了异步方法以及做了一些其他处理之后,原始线程就中断并且等异步方法完成...
    99+
    2023-01-28
    C# BeginInvoke C#异步编程 C# BeginInvoke异步编程
  • Node.js异步编程有哪些及怎么实现
    今天小编给大家分享一下Node.js异步编程有哪些及怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Node.js 的...
    99+
    2023-07-04
  • 【异步】Java 的 8 种异步实现方式
    异步执行对于开发者来说并不陌生,在实际的开发过程中,很多场景多会使用到异步,相比同步执行,异步可以大大缩短请求链路耗时时间,比如:发送短信、邮件。 异步的八种实现方式: 线程异步 Thread/Run...
    99+
    2023-09-08
    java spring 开发语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作