能否安全地同步 Java 方法参数?

取此代码:

public class MyClass {
    private final Object _lock = new Object();
    private final MyMutableClass _mutableObject = new MyMutableClass()

    public void myMethod() {
        synchronized(_lock) { // we are synchronizing on instance variable _lock
            // do something with mutableVar 
            //(i.e. call a "set" method on _mutableObject)
        }
    }
}

现在,想象一下将 myMethod() 中的代码委托给某个帮助程序类,在那里你传递了锁。

public class HelperClass {
    public helperMethod(Object lockVar, MyMutableClass mutableVar) {
        synchronized(lockVar) { // we are now synchronizing on a method param, 
                                // each thread has own copy
            // do something with mutableVar 
            // (i.e. call a "set" method on mutableVar)
        }
    }
}

是否可以通过传递其锁变量来重写“myMethod”以使用HelperClass,以便一切都是线程安全的?即

public void myMethod() {
    _helperObject.helperMethod(_lock, _mutableObject);
}

我不确定这一点,因为Java将按值传递lockVar,并且每个线程都将获得lockVar的单独副本(即使每个副本都指向堆上的同一对象)。我想问题归结为“synchronized”关键字的工作原理 - 它是否锁定了变量,还是锁定了变量引用的堆上的值?


答案 1

同步是在对象上完成的,而不是在变量上完成的。

变量/成员 [有时] 包含对象,并且 [变量] 中包含的结果对象实际上是在 中同步的。xsynchronized(x)

变量的线程可见性还有其他一些问题(例如,可能从变量中读取“过时”对象),但这在这里不适用:没有重新分配,并且保证了初始(“最终”)分配的可见性。因此,可以保证,在这种情况下,方法参数将始终包含用于同步的正确(相同)对象_lock

但是,如果使用的锁定对象(可能不是最终对象)发生更改,则需要重新评估适当的值/线程可见性,但除此之外,这与任何跨线程访问都没有区别。_lock

快乐编码。


答案 2