修复线程不安全的Java代码的策略?

时间:2012-12-14 12:56:44

标签: java multithreading static thread-safety

我必须修复一些包含恶意错误的旧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

1 个答案:

答案 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);
   ...
}

另一个也许更好的选择是从头开始重启。因为使用大量静态变量的多线程代码可能不值得保留,并且可能包含许多其他错误或不良做法。