使用Spring导致服务器内存上的单个类文件导致问题

时间:2018-01-22 10:03:56

标签: java spring spring-mvc design-patterns

我不太清楚如何解释这种情况,我会尽量保持清醒。

我目前正在编写一个Web应用程序,使用Spring来管理bean。显然,不止一个人会使用这个应用程序。每个用户都有一组与他自己相关的数据。我的问题来自于我刚刚进入开发领域时引入的一些糟糕的设计。情况就是这样:

@Component
public class ServiceClass implements IService    {

@Autowired
private Dependency   firstDependency;

@Autowired
private UsefulObject secondDependency;

private DataSet      dataSet; // THIS LINE IS IMPORTANT

public void entryPoint(String arg1, int arg2, Structure arg3)    {
    /* Query data from a database specific from the project (not SQL
    oriented. I absolutely need this information to keep going. */
    dataSet = gatherDataSet(String ar1);

    /* Treat the data */
    subMethodOne(arg1);
    subMethodTwo(arg2);
    subMethodThree(arg3);
}

private subMethodOne(String arg1)    {
    // Do some things with arg1, whatever
    subSubMethod(arg1);
}

private subSubMethod(String arg1)    {
    /* Use the DataSet previously gathered */
    dataSet.whateverDoing();
}

... // Functions calling sub-methods, using the DataSet;

由于每个用户都有不同的dataSet,我认为最好在每次调用我的服务时调用它。同样地,就像它在调用层次结构中使用得非常深,我认为将它存储为属性是个好主意。

我遇到的问题是,当两个用户几乎同时通过此服务时,我遇到了跨数据问题。发生以下情况:

  • 第一位用户进来,致电gatherDataSet
  • 第二位用户进来,致电gatherDataSet。第一个用户仍在治疗!
  • 第一个用户仍然使用第二个用户覆盖的dataSet对象。

基本上,第一个用户使用的数据变为虚假,因为他使用来自第二个用户的数据,这些数据在他之后很短暂。

我的问题如下:

  • 是否有设计模式/方法来避免这种行为?
  • 你可以配置Spring,以便他为两个用户使用两个实例(等等),以避免这种kinf od问题吗?

奖金:(无关的类型)如何实现一个非常大的数据映射器?

4 个答案:

答案 0 :(得分:1)

最简单的解决方案就是dataset存储在类字段中。

相反,将dataset存储在局部变量中并将其作为参数传递给其他函数,这样就不会出现任何并发问题,因为每个调用堆栈都会拥有它自己的实例。

示例:

public void entryPoint(String arg1, int arg2, Structure arg3) {
    // Store the dataset in a local variable, avoiding concurrency problems
    Dataset dataSet = gatherDataSet(String ar1);

    // Treat the data passing dataset as an argument
    subMethodOne(arg1, dataset);
    subMethodTwo(arg2, dataset);
    subMethodThree(arg3, dataset);
}

答案 1 :(得分:1)

对象成员变量(字段)与对象一起存储在堆上。因此,如果两个线程在同一对象实例上调用方法,并且此方法更新对象成员变量,则该方法不是线程安全的。

但是,如果资源是在同一个线程的控件内创建,使用和处理的,并且永远不会逃避对该线程的控制,那么该资源的使用是线程安全的。

考虑到这一点,改变你的设计。 https://books.google.co.in/books?isbn=0132702258是一本必读的书,用于提供基于java的良好软件设计

更多stackoverflow链接:Why are local variables thread safe in JavaInstance methods and thread-safety of instance variables

Spring提升单例模式和(它是默认的bean范围)。为两个不同用户提供两个服务类对象的Spring配置称为原型bean范围,但应尽可能避免。

考虑使用内存映射或外部no-sql数据存储区或外部关系数据库

答案 2 :(得分:1)

  

你可以配置Spring,以便他为两个用户使用两个实例(依此类推),以避免这种kinf od问题吗?

您已经正确地提到过,您所做的设计决策存在缺陷。但要回答您的具体问题,这应该让您的用例正常工作,但会对性能成本产生影响:

您可以将spring bean设置为各种范围(与您的用例相关:原型/请求或会话),这将在spring bean实例化时进行修改。默认行为是每个spring容器(单例)一个bean,因此出现并发问题。见https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html

答案 3 :(得分:-1)

使用synchronized修饰符。 在"同步在多个线程倾向于共享相同资源的应用程序中起着关键作用,特别是如果这些资源必须保持某种敏感状态,其中多个线程同时进行的操作可能导致资源变为一个不一致的状态。"

public void someMethod() {

  synchronized (object) {
    // A thread that is executing this code section
    // has acquired object intrinsic lock.

    // Only a single thread may execute this
    // code section at a given time.
  }

}