Spring 组件类必须是线程安全的

Must Spring component classes be thread-safe

提问人:Raedwald 提问时间:5/28/2013 最后编辑:Raedwald 更新时间:4/29/2023 访问量:32981

问:

如果你使用 Spring,你的组件类 (, , ) 必须是线程安全的吗?或者 Spring 是否以线程安全的方式使用它们,这样您就不必担心线程安全?@Controller@Service@Repository

也就是说,如果我的 中有一个方法,是否可以由多个线程同时为同一个控制器对象调用该方法?@RequestMapping@Controller

这之前有人问过,但没有这样回答)。

Java Spring 线程安全

评论

0赞 Sotirios Delimanolis 5/28/2013
关于编辑。Spring 将在同一类实例上调用相同的方法,以执行 所需的相同格式的请求。@Controller@RequestMapping

答:

0赞 duffymo 5/28/2013 #1

是的,当然。

最好是它们是无状态的,这样默认情况下它们线程是安全的。如果没有共享的可变状态,则没有问题。

评论

7赞 Raedwald 5/28/2013
“当然”:我认为这对初学者来说并不那么明显。想扩大一点吗?
0赞 Sotirios Delimanolis 5/28/2013
@Raedwalk Spring 只创建类的一个实例(默认情况下),因此每个请求将共享它们的所有实例字段。@Component
1赞 duffymo 5/28/2013
默认情况下,Spring 将 bean 设置为单例,但还有其他作用域:原型、每个请求、每个会话。其他有经验的开发人员可能会做出不同的选择。我更喜欢无状态的单例,因为我认为它们可以更好地扩展。
1赞 Raedwald 5/28/2013
“如果没有共享的、可变的状态”,几乎所有有趣的 Web 应用程序都有共享的、可变的数据。在大多数情况下,共享数据位于数据库中,因此确保线程安全的工作是在 Hibernate/JPA 中完成的。
1赞 duffymo 5/28/2013
大多数有趣的 Web 应用程序都没有共享状态。它们在会话中保持会话特定信息的安全,只有特定用户才能看到它。我不认为你明白线程安全意味着什么。
57赞 Sotirios Delimanolis 5/28/2013 #2

鉴于

@Controller
public class MyController {
    @RequestMapping(value = "/index")
    public String respond() {
        return "index";
    }
}

Spring 将创建一个 的实例。这是因为 Spring 解析了您的配置,看到(类似于)并实例化带注释的类。因为它也看到,所以它为它生成一个,请参阅此处的文档MyController<mvc:annotation-driven>@Controller@Component@RequestMappingHandlerMapping

接收到的任何 HTTP 请求都将通过之前注册的 Send 到此控制器实例,通过该实例上的 java 反射调用。DispatcherServletHandlerMappingrespond()

如果您有类似

@Controller
public class MyController {
    private int count = 0;
    @RequestMapping(value = "/index")
    public String respond() {
        count++;
        return "index";
    }
}

count将是一种危险,因为它可能会被许多线程修改并且对它的更改可能会丢失。

您需要了解 Servlet 容器的工作原理。容器实例化 Spring MVC 的一个实例。该容器还管理一个线程池,它使用它来响应连接,即。HTTP 请求。当这样的请求到达时,容器从池中选取一个线程,并在该线程中执行该方法,该方法调度到 Spring 为您注册的正确实例(从您的配置中)。DispatcherServletservice()DispatcherServlet@Controller

所以是的,Spring MVC 类必须是线程安全的。为此,您可以为类实例字段使用不同的作用域,或者只使用局部变量。如果做不到这一点,则需要在代码中的关键部分周围添加适当的同步。

1赞 Codo 4/15/2014 #3

默认情况下,控制器是单例的,因此必须是线程安全的。但是,您可以将控制器配置为请求或会话范围,即:

@Controller
@Scope("session")
public class MyController {

    ...
}

具有会话范围的控制器有助于管理会话状态。可以在 Spring-MVC 中使用会话(包括“作用域代理”)如何在 Spring MVC 中获取会话对象中找到对不同模式的良好描述。介绍的一些模式需要请求范围

如果您的数据无法承受每个请求计算多个数据,则请求范围也很有用。

评论

0赞 Tahir Hussain Mir 12/24/2016
你好。我来到这里,发现这很有用。但是你能告诉我,如果有很多用户说几百或几千。这是否意味着我们将拥有那么多的控制器?请帮忙!@codo
1赞 Codo 12/25/2016
我想是的。否则它将无法正常工作。如果您需要会话数据,则不可避免地会在内存中拥有每个用户的数据结构。
0赞 Donal Fellows 6/28/2022
会话范围的 Bean 仍应是线程安全的(根据需要),因为可以在单个会话中同时处理多个请求。
-1赞 Simon 2/14/2018 #4

如果一个 Spring 组件以及另一个类可以由多个线程同时使用,则它必须是线程安全的。默认情况下,Spring 组件是单例,可以从不同的线程调用。从这个角度来看,它的 STATE 应该是线程安全的。 另一方面,单例 Spring Bean 通常在线程池的线程调用堆栈中提供请求。通常,Spring 组件具有字段,这些值是注入的接口或属性。唯一需要注意安全的是当状态对象保存在 Bean 中时。通常,由于服务器已进行负载平衡,并且状态保存在缓存或数据库中,因此不会使用它。 因此,单例 Bean 应该用作一组函数,它将数据作为相关方法的参数进行传输,并且其字段不应保留业务数据,而应仅保留用于调用进度的注入 Bean。 Spring 的控制器就是一个例子。它的方法由Tomcat的线程调用,控制器方法映射的方式,所有的调用堆栈都在一个线程中完成。控制器的方法调用 DAO 方法并在同一线程中返回结果。 会话通常从一个会话持久化到另一个会话,这由 Spring 处理。 因此,除了极少数情况外,没有必要注意螺纹安全。