V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yodhcn
V2EX  ›  Java

Spring 的 @Async 注解,和自己手动用线程池执行 runnable 有什么区别?

  •  
  •   yodhcn ·
    yodhcn · 2023-05-24 19:59:59 +08:00 · 2555 次点击
    这是一个创建于 548 天前的主题,其中的信息可能已经有所发展或是发生改变。
    1. Spring 的 @Async 注解,和自己手动用线程池执行 runnable 有什么区别?

    2. Java 的“多线程”和“异步”的又有什么区别?

    3. 直到 JDK19 才有协程 /虚拟线程,“协程 /虚拟线程”与“异步”的联系是什么?

    4. 为什么有人说 “一处用异步,必须处处用异步。” —— 是因为不全用异步就会阻塞?还是因为不全用异步就没有意义?
      以及为什么有人说 “异步写法,相较于同步多线程模型,写起来更繁琐”?

    5. @Async 能和 Serverlet 一起用吗?@Async 与 Spring Reactive 有关系吗?

    以上就是我的疑问。

    • 楼主只学过 SSM ,对 Spring Reactive 不太了解。

    • 楼主学过一点 nodejs 和 nestjs ,比较习惯 JavaScript 的 async/await 这种写法,切换到 Java 多线程有点被绕晕了。
      Spring 的这个 @Async 注解让我搞不清“多线程”、“异步”和“协程”的区别。因为 nestjs 框架很大一部分参考了 spring ,本来以为 Java 这边也有 async/await ,但实际上直到 JDK19 才有协程 /虚拟线程,并且 Spring 现在主要还在用 JDK17

    7 条回复    2023-06-01 17:39:40 +08:00
    chuck1in
        1
    chuck1in  
       2023-05-24 20:16:08 +08:00   ❤️ 1
    一处用异步必须处处用异步,这个概念对多线程的语言不是那么贴切。
    yazinnnn
        2
    yazinnnn  
       2023-05-24 21:50:55 +08:00 via Android   ❤️ 1
    多线程和异步没啥关系,单线程也可以异步,js 不就是单线程么

    异步的概念是不会阻塞当前代码执行,可以新开一个线程或协程,也可以调用非阻塞的代码让他返回 future/promise/task/defer 之类的

    处处异步是为了适应你选用的异步框架,如果不在乎性能你可以处处 block

    不只异步代码写着累,在没有 monad comprehension 能力的语言里去编排 future ( monad )都是一件很痛苦的事,比如你写 java 时遇到很多层 null 需要判断,用 optional 去写同样痛苦,可能就好一点点
    MakHoCheung
        3
    MakHoCheung  
       2023-05-24 22:43:23 +08:00   ❤️ 1
    1. 没区别,Spring 的只是声明式。
    2. 多线程只是实现异步的一种方式。另外事件轮询也可以实现异步
    3. 协程 /虚拟线程也是实现异步的一种方式,底层其实就是(类似吧)事件轮询
    4. “一处用异步,必须处处用异步” 我也不理解。异步写法有很多种比如 Future 、回调、Delegate (苹果很喜欢),这些写法不像同步代码由上到下那样直观,反而是不知道跳到哪里去了,有点类似 goto 语句
    5. @Async 和 Servlet 肯定可以一起用,Spring MVC 底层就是 Servlet 。不熟 Spring Reactive ,答不了
    zu1y
        4
    zu1y  
       2023-05-24 23:26:32 +08:00   ❤️ 1
    Async 注解和 SpringReactive 没关系

    用最新的 Servlet3.1 + WebFlux 就是 Reactive 模式
    lyxeno
        5
    lyxeno  
       2023-05-25 10:48:14 +08:00   ❤️ 1
    Java 的异步写法确实很复杂。

    4. 没有一处用异步,处处用异步的说法,这个说法是错误的。我觉得它指的是反应式编程( Reactive )时如果使用了阻塞式编程,非阻塞的收益就变得很小。这个说法来自 Spring 的官方文档。
    > A simple way to evaluate an application is to check its dependencies. If you have blocking persistence APIs (JPA, JDBC), or networking APIs to use, then Spring MVC is the best choice for common architectures at least. It is technically feasible with both Reactor and RxJava to perform blocking calls on a separate thread but you wouldn’t be making the most of a non-blocking web stack.
    评估应用程序的一种简单方法是检查其依赖项。如果你有阻塞持久性 API ( JPA ,JDBC )或网络 API 可以使用,那么 Spring MVC 至少是常见架构的最佳选择。从技术上讲,Reactor 和 RxJava 在单独的线程上执行阻塞调用是可行的,但你不会充分利用非阻塞 Web 堆栈。

    5. 可以一起用。没关系
    yodhcn
        6
    yodhcn  
    OP
       2023-05-25 12:20:50 +08:00
    @lyxeno #5 原来是这么一回事啊。感谢老哥的耐心解答,但我还有疑问:

    6. 所谓的 “阻塞式编程” 与 “反应式编程”,它们的主要区别在哪里?体现在哪些地方?
    7. 为什么说 JDBC 是 “阻塞性 API”?
    8. “反应式编程” 与 “多线程” 有什么联系吗?
    Belmode
        7
    Belmode  
       2023-06-01 17:39:40 +08:00   ❤️ 1
    6. “阻塞式编程”指的是程序在进行 IO 操作时,当前的工作线程只能被动等待 IO 操作完成,然后才能继续往下执行。在等待的时间内,当前线程实际上是被阻塞的。“反应式编程”是借助于基于异步事件的 IO (或者就真的是异步 IO )这种方式,可以在工作线程进行 IO 操作时,让出当前线程,去服务其他用户或者完成其他工作,等待 IO 完成后,再继续完成接下来的工作。这就是它们的主要区别。
    核心就是:当前工作线程会在 IO 操作时,去做其他工作,等 IO 完成后继续做剩下的工作。

    7. JDBC 这个连接驱动,是基于“阻塞 IO”来访问数据库的,每个工作线程在和数据库通信时,都是被动阻塞的,无法服务于其他任务。( R2DBC 是基于异步事件 IO 的)

    8. “反应式编程” 和 “多线程”没有直接联系,比如 js 单线程,但它依然可以是“反应式的”。反应式的核心是在 IO 操作阻塞时,可以让出当前工作线程,等 IO 完成,由操作系统通知,当前工作线程继续完成剩下任务。Java 的反应式框架,在实现上,肯定是需要多线程支持的。不然,没有多线程,就“异步”这个操作都无法实现。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2788 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 13:42 · PVG 21:42 · LAX 05:42 · JFK 08:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.