实例方法和实例变量的线程安全

2022-09-01 18:27:49

我想知道一个类的每个实例是否都有它自己的类中方法的副本?

比方说,我有以下类:MyClass

public MyClass {

    private String s1;

    private String s2; 

    private String method1(String s1){
    ...
    }

    private String method2(String s2){
    ...
    }
}

因此,如果两个不同的用户创建一个实例:MyClass

MyClass instanceOfUser1 = new MyClass();
MyClass instanceOfUser2 = new MyClass();

是否知道每个用户在他的线程中都有一个方法的副本?如果是,则实例变量是线程安全的,只要只有实例方法操作它们,对吧?MyClass

我问这个问题是因为我经常读到实例变量不是线程安全的。我不明白为什么应该这样,当每个用户通过调用运营商获得一个实例时?new


答案 1

每个对象都获取类的实例变量的自己的副本 - 它是在类的所有实例之间共享的变量。实例变量不一定是线程安全的,原因是它们可能同时被调用不同步实例方法的多个线程修改。static

class Example {
    private int instanceVariable = 0;

    public void increment() {
        instanceVariable++;
    }
}

现在,如果两个不同的线程同时调用,那么你就有了一个数据竞争 - 在两个返回的方法结束时可能会递增1或2。您可以通过将关键字添加到 ,或者使用 a 而不是 , etc 来消除这种数据竞争,但关键是,仅仅因为每个对象都获得了类的实例变量的副本,并不一定意味着变量是以线程安全的方式访问的 - 这取决于类的方法。(例外情况是不可变的变量,它不能以线程不安全的方式访问,除非像序列化黑客这样愚蠢的东西。incrementinstanceVariablesynchronizedincrementAtomicIntegerintfinal


答案 2

多线程问题主要出现在同时访问的静态变量和类的实例上。

您不必担心类中的方法,而应更多地担心字段(意味着在类级别限定范围)。如果存在对类的实例的多个引用,则不同的执行路径可能会尝试同时访问该实例,从而导致意外后果,例如争用条件。

类基本上是用于创建对象实例的蓝图。当对象实例化时,它会接收到内存中由引用访问的点。如果多个线程具有此引用的句柄,则可能导致同时访问实例的情况,这将导致两个线程都操作字段。