拥有这个简单的类,具有加法方法:
class A {
public Integer add (int a, int b){
return a+b;
}
}
它是否是线程安全的?在我看来它是安全的,但大多数人回答不安全,有人能解释一下为什么吗?
class A {
public Integer add (int a, int b){
return a+b;
}
}
它是否是线程安全的?在我看来它是安全的,但大多数人回答不安全,有人能解释一下为什么吗?
当您有某种共享状态的方式并且在没有任何锁定或同步的情况下对其进行修改时,即修改共享变量(类级变量),那么只有在这种情况下才需要考虑线程安全。
这里不存在线程安全问题。这是完全线程安全的,因为所有变量都是本地的。
实际上,该方法不是线程安全的,但需要您了解一些关于 Integer
类内部的知识才能理解为什么。让我们看一下产生相同字节码的一些代码:
class A {
public Integer add (int a, int b){
// auto boxing hidden in OP's implementation
return Integer.valueOf(a+b);
}
}
Integer
会被缓存并在数组中查找。使用反射可以访问该数组并更改其元素。但是这些更改不是同步的,因此如果您从另一个线程更改这些元素,则您的方法的结果也会发生变化。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
使用的共享状态从未被修改。因此,在这种情况下,它将是线程安全的。
Integer
类,使得Integer
对象的值与通常预期的不同。也就是说,他正在黑客攻击它以返回错误的值,然后他说,要小心,因为他的黑客攻击在不同的线程中不一定会产生相同的错误值。“反射”不是问题所在。问题在于他使用反射来做什么。 - Solomon Slow