有了这个简单的类,加上方法:
class A {
public Integer add (int a, int b){
return a+b;
}
}
是不是线程安全..?对我来说看起来很安全,但大多数人回答不,有人可以解释原因吗?
答案 0 :(得分:3)
线程安全只有当你有一些共享状态的方法时才会被打扰,你修改它没有任何锁或同步,即你修改共享变量(类级变量),那么只有你应该关心线程安全。
这里没有螺纹安全问题。
并且在该特定情况下每个变量是本地和该位置不会被线程共享的每个函数调用将具有其上堆栈上单独分配与他们的局部变量的沿着不应打扰反正:) < / p>
答案 1 :(得分:2)
它完全是线程安全的,因为所有变量都是本地的。
答案 2 :(得分:2)
实际上该方法不是线程安全的,但它需要您了解Integer
类的内部以了解原因。让我们看一些产生相同字节码的代码:
class A {
public Integer add (int a, int b){
// auto boxing hidden in OP's implementation
return Integer.valueOf(a+b);
}
}
对于足够小的值,Integer
被缓存并在数组中查找。使用反射,您可以访问该数组并更改其元素。 这些更改未同步,因此如果您更改这些元素,则从另一个线程中,您的方法的结果也会发生变化。
以下代码应该演示大多数Java VM上的问题:您的方法中存在竞争条件。在大多数情况下,它将打印4s和5s:
import java.lang.reflect.Field;
class A {
public Integer add(int a, int b) {
return a + b;
}
private static volatile boolean cont = true;
public static void main(String[] args) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InterruptedException {
final A a = new A();
new Thread(() -> {
while(cont) {
for (int i = 0; i < 100; i++) {
// print result of add method
System.out.println(a.add(2,2));
}
}
}).start();
// give other thread time to start
Thread.sleep(1);
// mess around with the internals of Integer
Class cache = Integer.class.getDeclaredClasses()[0];
Field c = cache.getDeclaredField("cache");
c.setAccessible(true);
Integer[] array = (Integer[]) c.get(cache);
array[132] = array[133];
cont = false;
}
}
然而,在大多数情况下,没有人会因Integer
的内部而烦恼。如果永远不修改Integer
类中的数组,则由方法返回的Integer
对象包装的值将始终正确,因为Integer.valueOf
使用的共享状态永远不会被修改。因此,在这种情况下它将是线程安全的。