我不太清楚如何解释这种情况,我会尽量保持清醒。
我目前正在编写一个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
对象。基本上,第一个用户使用的数据变为虚假,因为他使用来自第二个用户的数据,这些数据在他之后很短暂。
我的问题如下:
奖金:(无关的类型)如何实现一个非常大的数据映射器?
答案 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 Java,Instance 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.
}
}