要同步哪些对象?为啥局部变量不好? [复制]

Posted

技术标签:

【中文标题】要同步哪些对象?为啥局部变量不好? [复制]【英文标题】:What objects to synchronize on? Why are local variables bad? [duplicate]要同步哪些对象?为什么局部变量不好? [复制] 【发布时间】:2020-11-27 06:47:42 【问题描述】:

关于同步的堆栈溢出有很多资料,但我仍然没有获得关于决定将哪个对象用作内在锁的高质量内容。根据经验,有人真的能做出一个好的答案吗?

那么我应该选择“监视器”作为实例变量还是局部变量或拥有该方法的实例?他们三个都很好地完成了这项工作。原始值包装类也使用“池”,因此也没有问题,因为线程“攻击”相同的锁。

那么为什么这样做更好(这个):

class A 
    void methodA()
        synchronized (this)
            //some code    
        
    

对此(实例变量):

class A 
    String monitor = "monitor";
    void methodA()
        synchronized (monitor)
            //some code
        
    

或者超过这个(局部变量):

class A 
    void methodA()
        String monitor = "monitor";
        synchronized (monitor)
            //some code
        
    

它们都工作正常/相同。那么为什么我读到当局部变量隐式使用池来存储对象时我应该避免使用它们呢?在这种情况下,变量的范围有什么影响?

谢谢!

【问题讨论】:

他们“都工作正常/相同”。在局部变量上同步完全没有任何作用,因为每个调用者都有自己的值。在实例成员而不是实例上进行同步可以让您进行更细粒度的控制:您可以为不同的操作设置不同的监视器,而不会相互干扰。 【参考方案1】:

您应该避免使用存储在局部变量中的对象的监视器,因为通常只有当前线程可以访问存储在局部变量中的对象。但由于在这种特殊情况下,局部变量实际上保存了一个来自常量池的全局共享对象,因此您不会遇到这个特殊问题。

使用常量池对象监视器的问题如下:

String monitor = "monitor";
void methodA() 
    synchronized (monitor)
        //some code
    

...是只有一个池化的常量对象。

在 A 类的两个不同实例上运行的两个不同线程不能同时进入 methodA 中的同步块,即使您已确保它应该是安全的(例如,您不触摸静态共享状态) )。

更糟糕的是:其他地方可能还有其他一些 B 类,它也恰好在常量“monitor”字符串上同步。现在使用 B 类的线程将阻止其他不相关的线程使用 A 类。

最重要的是,创建deadlock 非常容易,因为您会在不知不觉中在线程之间共享锁。

【讨论】:

我能问你为什么在你的第一段中说它应该是安全的吗?在给定的情况下,我们将使用该方法同步所有实例,而不仅仅是使用 (this) 同步一个实例。如果我们有所有线程共享的静态变量怎么办?然后它会破坏程序,不是吗?这就是为什么我们需要将“通用”实例变量作为“监视器”。我说的对吗? 是的,在这种情况下,您需要一个对类的所有实例都通用的锁。我已经对声明进行了澄清。

以上是关于要同步哪些对象?为啥局部变量不好? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的函数使用局部变量而不是全局变量? [复制]

为啥局部变量会自我更新? [复制]

为啥即使使用 setlocal enabledelayedexpansion 也无法访问 for 循环(批处理文件)中的局部变量? [复制]

为啥我们要使用自动类型的存储类? [复制]

JS全局变量是全局对象的属性,函数局部变量为啥就不是函数的属性呢?

为啥要为局部变量保留堆栈空间?