您可以安全地同步 Java 方法参数吗?

Posted

技术标签:

【中文标题】您可以安全地同步 Java 方法参数吗?【英文标题】:Can you safely synchronize on a Java method parameter? 【发布时间】:2011-09-06 18:29:31 【问题描述】:

获取此代码:

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 副本(即使每个副本都指向堆上的同一个对象)。我想问题归结为“同步”关键字的工作原理——它是锁定变量还是变量引用的堆上的值?

【问题讨论】:

【参考方案1】:

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

变量/成员 [有时] 包含 objects,它是包含在 [variable] x 中的结果 object 实际上在 synchronized(x) 中同步。

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

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

编码愉快。

【讨论】:

实际上,我的例子并不正确。我并不是要将 _mutableObject 标记为最终的。我不认为这种锁定会起作用 IF 父对象中的 _mutableObject 是非最终的。如果将该值作为变量传递给辅助方法,然后将 _mutableObject 更改为指向父类中的其他内容,则当辅助方法运行时,它不再保留 _mutableObject 的正确引用。锁仍将锁定,但您将“保护” _mutableObject 的陈旧值。这确实是我的问题应该指出的。 我会接受您的回答,假设这仅在父类中 _mutableObject 为“final”时才有效。无论您使用我的示例的哪个版本,_lock 对象始终需要声明为 final。事实上,我不应该创建我的示例来包含“可变”对象。我应该刚刚描述了我想保护一个非最终变量,称之为“私有对象_object”。我想要锁来保护这个值的变化。如果将示例更改为该示例,我认为您不能将锁定传递给辅助方法,因为辅助方法会获取陈旧的 _object 引用。 @android Dev 用于锁定的对象 object 对于所需的每个互斥同步应该是稳定且唯一的。受同步保护的变量(或对象)可以更改(同步上下文将确保内存屏障和发生前的保证,只要所有此类同步都在同一个锁对象上操作)。交换用于锁定的对象通常是一个坏主意(锁定 object 可以改变,这不会影响它与 synchronized 的使用——重要的是 相同的对象 用于同步。) 我没有交换用于锁的对象。我正在用锁交换我正在保护的对象。我理解您所说的关于锁 object (非变量)上发生的同步的内容。但是,如果我试图通过使用锁来保护实例变量,我认为我不能编写一个辅助方法并将该变量传入(使用锁,我可以传入),因为一旦我通过作为参数的变量,它可以被另一个线程更改。在传递给辅助方法之前,我需要在锁上同步。这就是我需要解决的方法。

以上是关于您可以安全地同步 Java 方法参数吗?的主要内容,如果未能解决你的问题,请参考以下文章

不安全的compareAndSwapInt与同步

这是同步代码块的安全方法吗?

安卓和 java 学习笔记

我可以将数组作为参数传递给 Java 中具有可变参数的方法吗?

58synchronized同步方法

如何安全地为 bigquery 节点插入转义用户输入?可以在 bigquery.insert 节点库上使用参数化查询吗?