【问题标题】:How to make Spring beans thread-safe如何使 Spring bean 线程安全
【发布时间】:2013-02-06 18:36:00
【问题描述】:

据我所知,Spring bean 默认是单例的。

我想要的是考虑实例属性使 bean 成为线程安全的。 我将尝试使用一个简单的示例向您展示。
考虑以下代码:

@Controller
public class MyServlet {

    @Autowired
    private HelloService service;

    @RequestMapping(value="/hello", method = RequestMethod.GET)
    public void sayHello(HttpServletRequest req, HttpServletResponse res) throws IOException {
        service.doStuff();
    }

}


public class HelloService {

    private int i = 1;

    public void doStuff() {
        System.out.println("Started " + i);
        i++;
        System.out.println(Thread.currentThread().getName() + " Done " + i);
    }
}

输出将是这样的:

32911580@qtp-28064776-0 - Started 1
7802158@qtp-28064776-2 - Started 2
32911580@qtp-28064776-0 - Done 3
7802158@qtp-28064776-2 - Done 3

这证明“i”变量在多个线程之间共享。

我也尝试将 HelloService bean 定义为原型,像这样

<bean id="helloService" class="my.package.HelloService" scope="prototype" />

但结果是一样的。

我发现解决此问题的唯一方法是: - 将声明移到 doStuff() 方法中,但这不是我想要的 - 制作 doStuff() 方法,但这意味着有锁

我想要的是在每次调用时都有一个新的 HelloService 实例。

谁能帮帮我? 提前致谢。

更新

我使用查找方法通过方法注入找到了解决方案。 http://static.springsource.org/spring/docs/3.1.1.RELEASE/spring-framework-reference/html/beans.html#beans-factory-lookup-method-injection

【问题讨论】:

  • 这不是一个现实的例子。在 99% 的情况下,Spring 服务是无状态的。你有一个有状态的 Spring 服务的具体实例吗?
  • 也许您可以尝试将 i 声明为 volatile。不确定这是否能解决您的问题。
  • Spring 部分无关紧要。您在问如何使代码线程安全。

标签: spring thread-safety code-injection autowired


【解决方案1】:

由于您只有一个 MyServlet 实例,因此您也将只有一个 HelloService 实例。

以下弹簧作用域之一有帮助: request 每个 HTTP 请求的一个新实例。 session 在 servlet 容器中创建的每个新 HttpSession 的类的新实例。

这将改变代码的语义。如果您在范围请求中声明 bean,您的计数器将始终为 1。

要拥有一个没有锁的唯一计数器,您可以使用 java.util.concurrent.atomic.AtomicInteger。

另一方面,锁也没什么不好。除极端情况外,大多数情况下对性能的影响都可以忽略不计。

在您构建应用程序后,使用http://vmlens.com 检查竞争条件。

【讨论】:

    【解决方案2】:

    除了 Thomas 的回答之外,对于服务 bean,请求范围是有状态和线程安全组件的最佳选择,因为在该范围内通常只调用一个实例。

    如果在特定应用范围内需要多个(有状态和线程安全)bean,原型范围将是最合适的选择。

    【讨论】:

      【解决方案3】:

      我也尝试过将 HelloService bean 定义为原型,但结果是一样的。

      您得到了相同的结果,因为您的 MyServlet 是单例的。所以你只有一个MyServlet 实例,因此你只有一个HelloService 实例。如果您也将MyServlet 设为 prototype bean,那么每次都会获得两者的新实例

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-07-06
        • 1970-01-01
        • 2013-09-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-19
        • 2011-12-07
        相关资源
        最近更新 更多