我必须修复一些包含恶意错误的旧java代码:
当用户登录代码时,通过查询需要一两秒钟的LDAP服务器来检查他的权限。
当另一个用户在该时间范围内登录时,似乎第一个用户的权限检查继续使用第二个用户的权限,这当然是一个灾难性的错误。似乎第一个线程中的数据被第二个线程覆盖。
整个代码中散布着许多静态变量和方法。我不知道是否有充分的理由让它们变得静止,或者我是否可以让它们变得动态。
你能推荐一个如何使这个代码线程安全的策略,或者关于这个一般类问题的教程?
以下是有关所发生情况的一些细节。首先,标准流程的一个例子:
2012-12-11 15:07:14,146 INFO [TP-Processor20] [MyListener] handleLoginEvent Login Event: username=[USER1]
2012-12-11 15:07:14,865 INFO [TP-Processor20] [MyListener] doInHibernate Group Maps Array has 3 maps inside.
2012-12-11 15:07:14,865 INFO [TP-Processor20] [MyListener] doInHibernate External Group NAME=[*] USER=[USER1] is a member? true
...
2012-12-11 15:07:16,036 INFO [TP-Processor20] [MyListener] doInHibernate External Group NAME=[ou:GROUP-A] USER=[USER1] is a member? false
...
2012-12-11 15:07:16,068 INFO [TP-Processor20] [MyListener] doInHibernate External Group NAME=[ou:GROUP-Z] USER=[USER1] is a member? false
TP-Processor20 done.
2012-12-11 15:07:33,099 INFO [TP-Processor9] [MyListener] handleLoginEvent Login Event: username=[USER1]
2012-12-11 15:07:33,677 INFO [TP-Processor9] [MyListener] doInHibernate Group Maps Array has 3 maps inside.
2012-12-11 15:07:33,677 INFO [TP-Processor9] [MyListener] doInHibernate External Group NAME=[*] USER=[USER1] is a member? true
...
2012-12-11 15:07:33,755 INFO [TP-Processor9] [MyListener] doInHibernate External Group NAME=[ou:GROUP-A] USER=[USER1] is a member? false
...
2012-12-11 15:07:33,786 INFO [TP-Processor9] [MyListener] doInHibernate External Group NAME=[ou:GROUP-Z] USER=[USER1] is a member? false
TP-Processor9 done.
以下是问题的日志摘录。请注意第二次登录后保存用户名的变量是如何不同的。
2012-12-11 15:07:53,082 INFO [TP-Processor9] [MyListener] handleLoginEvent Login Event: username=[USER2]
2012-12-11 15:07:53,661 INFO [TP-Processor9] [MyListener] doInHibernate Group Maps Array has 3 maps inside.
2012-12-11 15:07:53,676 INFO [TP-Processor9] [MyListener] doInHibernate External Group NAME=[*] USER=[USER2] is a member? true
note the handleLoginIvent from another user
2012-12-11 15:07:53,676 INFO [TP-Processor1] [MyListener] handleLoginEvent Login Event: username=[USER1]
...
note that the USER= value has changed to that of TP-Processor1. Also, the "is a member" test returns now true which is incorrect for user USER1. It is actually user USER2 who is a member of that group.
2012-12-11 15:07:53,832 INFO [TP-Processor9] [MyListener] doInHibernate External Group NAME=[ou:GROUP-A] USER=[USER1] is a member? true
...
2012-12-11 15:07:53,989 INFO [TP-Processor9] [MyListener] doInHibernate External Group NAME=[ou:GROUP-Z] USER=[USER1] is a member? false
TP-Processor9 done
2012-12-11 15:07:54,286 INFO [TP-Processor1] [MyListener] doInHibernate Group Maps Array has 3 maps inside.
2012-12-11 15:07:54,286 INFO [TP-Processor1] [MyListener] doInHibernate External Group NAME=[*] USER=[USER1] is a member? true
...
2012-12-11 15:07:54,364 INFO [TP-Processor1] [MyListener] doInHibernate External Group NAME=[ou:GROUP-A] USER=[USER1] is a member? false
...
2012-12-11 15:07:54,551 INFO [TP-Processor1] [MyListener] doInHibernate External Group NAME=[ou:GROUP-Z] USER=[USER1] is a member? false
答案 0 :(得分:3)
尝试删除每个静态或实例变量,并将其替换为从方法到方法作为参数传递的局部变量。因此,您的代码将变为无状态,因此是线程安全的。例如,替换以下
private static int foo;
public void bar() {
...
foo = someMethod();
...
baz();
}
private void baz() {
...
someOtherMethod(foo);
...
}
通过
public void bar() {
...
int foo = someMethod();
...
baz(foo);
}
private void baz(int foo) {
...
someOtherMethod(foo);
...
}
另一个也许更好的选择是从头开始重启。因为使用大量静态变量的多线程代码可能不值得保留,并且可能包含许多其他错误或不良做法。