当我们使用springboot构建服务的时候需要处理并发。一种错误的观念认为由于使用了Servlets,它对于每个请求都分配一个线程来处理,所以就没有必要考虑并发。在这篇文章中,我将提供一些建议,用于处理springboot中的多线程问题以及如何避免一些可能导致的情况。

spring boot 并发基础

当我们考虑springboot应用的并发的时候需要考虑的关键点有一下几个:

  • 最大线程数-也就是应用于处理请求的最大线程数
  • 共享外部资源-外部共享资源的调用,比如数据库
  • 异步方法调用-当某些调用等待返回的时候会释放线程到线程池中
  • 共享内部资源-内部资源的调用,比如说缓存和共享的应用状态

接下来我们会一个一个地解释它们,看看他们是从哪些方面影响我们使用springboot编写应用程序的。

springboot应用中的最大线程数

第一个需要引起注意的就是你正在使用的是有限的线程数量。

如果你使用的是Tomcat作为嵌入式服务器(默认),那么可以使用server.tomcat.max-threads属性来控制允许的线程数。它默认被置为0,这意味着默认使用的是tomcat本身的默认值200

知道这一点很重要,因为你可能需要增加这个数字以更有效地处理服务提供的资源。在处理外部资源时也会出现问题......

关于共享外部资源的问题

调用数据库和其他REST接口会导致可观的时间开销。

刚刚对于线程数的限制意味着你真的希望避免运行太久的、太慢的、同步的请求。如果你正在等待一些很慢的处理完成而持有线程,那么你很可能没有充分利用服务器。

如果存在很多长时间运行的线程在等待返回,你可能最终会遇到这样一种情况:真正快速、简单的请求等待很长时间,“永远等待”直到被终止。

那么怎么优化这种情况呢?

异步方法调用来拯救

一个通常很有用的方法是在一次请求中请求多个数据。理想情况下,如果你需要调用三个服务:A、B和C;你不会希望用以下方式:

  • 调用A
  • 等待A的返回
  • 调用B
  • 等待B的返回
  • 调用C
  • 等待C的返回
  • 把A、B、C的返回组合起来然后结束处理

如果每个服务需要花费3秒钟返回,那么整个处理过程需要9秒。更好的处理方式应该是这样:

  • 调用A
  • 调用B
  • 调用C
  • 等待A、B、C三个服务的返回
  • 把A、B、C的返回组合起来然后结束处理

这种方式中,请求三个服务的过程中不需要等待某个服务完成。如果假设A、B、C三个服务没有互相依赖的话,等待返回只需要3秒。

异步和响应式微服务的概念本身就十分有趣。我推荐阅读以下文章:

这些话题都非常引人入胜,但是现在我们继续关注Spring Boot......

在springboot中使用异步调用

你是怎么在springboot中开启异步方法调用的呢?首先可以在标注有@SpringBootApplication注解的应用类上使用@EnableAsync注解。

应用该注解之后,就可以在返回CompletableFuture<>的服务中使用@Async注解。因为使用了@EnableAsync之后,@Async标注的方法将会运行在后台的线程池中。

如果利用好了异步执行,在性能上将可以避免很多不必要的坑,让你的服务更快、更好的响应。

对于这方面在springboot中的详细实现我非常推荐这篇文章the example from the official Spring website

共享内部资源

虽然前面的部分讨论的是我们通常无法控制的外部资源,但是系统的内部资源却是我们能够完全控制的。

知道这一点后,避免共享资源所导致的问题的最佳建议就是——不要共享他们。

Spring的Services层和Controller层默认是单例的,这一点需要尤其关注和小心。一旦在你的服务中涉及到可变状态那么你就需要像处理其他标准应用一样处理它。

其他潜在的共享资源有缓存、自定义的服务器范围组件(通常是监控和安全组件)。

如果你不可避免的需要共享一些状态,我提供如下建议:

  • 使用不可变对象。如果你的对象是不可变的这样可以避免很多并发相关的错误。如果需要改变某些状态,直接创建一个新的对象就是了。
  • 了解你的集合类。并不是所有的集合类都是线程安全的,一个通常的陷阱就是把HashMap当成线程安全的来使用(如果需要并发访问,那么应该使用ConcurrentHashMap或者HashTable获取其他线程安全的解决方案)
  • 不要假设第三方库是线程安全的。大多数代码都不是,并且必须控制对共享状态的访问。
  • 如果你打算依赖共享--可以适当的学习一下并发相关的知识。我非常强烈的推荐《Java Concurrency in Practice》。虽然是2006年写的,但是在现在任然很实用。

总结

spring中的并发和多线程是非常重要的话题。在这篇文章中,我想强调一些当你编写springboot应用需要注意的关键领域。如果你想成功地构建高质量、高需求的服务,你需要围绕这个话题做出仔细地考量和权衡。我希望这篇文章能够成为一个很好的入门。

PS:本文翻译自https://www.e4developer.com/2018/03/30/introduction-to-concurrency-in-spring-boot/

[译]Introduction to Concurrency in Spring Boot的更多相关文章

  1. 1.spring boot起步之Hello World【从零开始学Spring Boot】

    [视频&交流平台] àSpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008&utm ...

  2. 0. 前言【从零开始学Spring Boot】

    [视频&交流平台] àSpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008&utm ...

  3. 0. 资料官网【从零开始学Spring Boot】

    [视频&交流平台] àSpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008&utm ...

  4. 43. Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】

    [视频&交流平台] àSpringBoot视频 http://study.163.com/course/introduction.htm?courseId=1004329008&utm ...

  5. Spring Boot使用模板freemarker【从零开始学Spring Boot(转)

    视频&交流平台: à SpringBoot网易云课堂视频 http://study.163.com/course/introduction.htm?courseId=1004329008 à  ...

  6. 译:Spring Boot 自动伸缩

    原文链接:https://dzone.com/articles/spring-boot-autoscaler 作者:Piotr Mińkowski 译者:helloworldtang 自动伸缩是每个人 ...

  7. 译自如何将Spring Cloud应用程序从Spring Boot 1.2迁移到1.3

    前言 笔者第三个Spring Cloud(版本为Spring Boot 1.2)类项目升级最新版本时遇到不少问题,本文内容是作者翻译Spring Cloud官网一位国外友人文章产生. 原文地址: Mi ...

  8. [译]Spring Boot 构建一个RESTful Web服务

    翻译地址:https://spring.io/guides/gs/rest-service/ 构建一个RESTful Web服务 本指南将指导您完成使用spring创建一个“hello world”R ...

  9. Spring Boot Maven Plugin打包异常及三种解决方法:Unable to find main class

    [背景]spring-boot项目,打包成可执行jar,项目内有两个带有main方法的类并且都使用了@SpringBootApplication注解(或者另一种情形:你有两个main方法并且所在类都没 ...

随机推荐

  1. 【游记】NOIP2019前传

    声明 我的游记是一个完整的体系,如果没有阅读过往届文章,阅读可能会受到障碍. ~~~上一篇游记的传送门~~~ 前言 比完赛后,我沉浸在胜利中长达半个月,而后才清醒过来,意识到自己需要为NOIP2019 ...

  2. Oauth2认证模式之授权码模式实现

    Oauth2认证模式之授权码模式(authorization code) 本示例实现了Oauth2之授权码模式,授权码模式(authorization code)是功能最完整.流程最严密的授权模式.它 ...

  3. Liunx查看后1000行的命令以及查看中间部分

    linux 如何显示一个文件的某几行(中间几行) [一]从第3000行开始,显示1000行.即显示3000~3999行 cat filename | tail -n +3000 | head -n 1 ...

  4. Kafka 系列(二)—— 基于 ZooKeeper 搭建 Kafka 高可用集群

    一.Zookeeper集群搭建 为保证集群高可用,Zookeeper 集群的节点数最好是奇数,最少有三个节点,所以这里搭建一个三个节点的集群. 1.1 下载 & 解压 下载对应版本 Zooke ...

  5. Day 01--选题与设计(一)

    1.第一天我们主要确定了软件课设的项目,做一个学校内食堂订送餐的微信小程序.我们大体的设计思路是:可以实现学生身份的认证,幷使学生们能自行选择校园内的食堂,挑选各个食堂各个窗口菜谱上可以选择的菜,选择 ...

  6. mysql数据库的水平拆分与垂直拆分

    近端时间在面试,发现很多面试官或者面试都把数据的水平拆分合垂直拆分给搞混了,今天特意写了一篇博客来说说水平拆分和垂直拆分希望对程序猿们有所帮助. 数据库水平与垂直拆分: 垂直(纵向)拆分:是指按功能模 ...

  7. Django-内置用户系统

    Django自带的用户认证 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统.此时我们需要实现包括 1.用户注册 2.用户登录 3.用户认证 4.注销 5.修改密码 Django作为一个 ...

  8. 既然synchronized是"万能"的,为什么还需要volatile呢?

    在我的博客和公众号中,发表过很多篇关于并发编程的文章,之前的文章中我们介绍过了两个在Java并发编程中比较重要的两个关键字:synchronized和volatile 我们简单回顾一下相关内容: 1. ...

  9. RabbitMQ简洁安装

    在实际开发过程中,为了解决并发量大的问题,我们往往会引入消息中间件这个杀手锏,今天带大家先入门一个消息中间件RabbitMQ,我们会从RabbitMQ安装.使用来分享. 1. RabbitMQ安装 这 ...

  10. com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect

    com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: con ...