简单的加法方法是否线程安全?

3
拥有这个简单的类,具有加法方法:
class A {
   public Integer add (int a, int b){
     return a+b;
   }
}

它是否是线程安全的?在我看来它是安全的,但大多数人回答不安全,有人能解释一下为什么吗?


https://dev59.com/GG015IYBdhLWcg3w6QA0 - Jude Niroshan
3个回答

3

当您有某种共享状态的方式并且在没有任何锁定或同步的情况下对其进行修改时,即修改共享变量(类级变量),那么只有在这种情况下才需要考虑线程安全。

这里不存在线程安全问题。
而且在这种特殊情况下,每个变量都是局部的,并且该位置不会被线程共享,因为每个函数调用将具有它们自己的分配在堆栈上以及它们的本地变量。无论如何,您都不必担心 :)

感谢提供详细信息。 - Nassim MOUALEK
你认为那个说法是正确的,但实际上并不是,可以看看这个Code Golf问题的答案:编写一个使2 + 2 = 5的程序 - fabian
1
从线程安全的角度来看,这是真正的线程安全代码。而你(@fabian)上面提到的反射代码,反射既不是为此而设计的,也不是其良好使用的方式。我同意你可以始终通过反射产生恶意行为。但这从未被推荐过。 - Shubham Chaurasia

2

这是完全线程安全的,因为所有变量都是本地的。


那正是我所想的。 - Nassim MOUALEK

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中演示问题:您的方法存在竞态条件。在大多数情况下,它将打印出4和5:
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 使用的共享状态从未被修改。因此,在这种情况下,它将是线程安全的。


那么我们可以说,除非我们搞乱了反射,否则它是安全的? - Nassim MOUALEK
@NassimMOUALEK,这不是“搞乱反射”。Fabian的例子是颠覆了Integer类,使得Integer对象的值与通常预期的不同。也就是说,他正在黑客攻击它以返回错误的值,然后他说,要小心,因为他的黑客攻击在不同的线程中不一定会产生相同的错误值。“反射”不是问题所在。问题在于他使用反射来做什么。 - Solomon Slow
好的,这个问题是一个招聘者的问题,我只是在问什么是最好的答案,我们不能说不是线程安全的,好的回答是,除非我们使用反射,否则它是线程安全的。 - Nassim MOUALEK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接