跨对象的Java同步

跨对象的Java同步,java,synchronization,Java,Synchronization,我试图确保我理解java中同步的性能含义。我有几个简单的类: public class ClassOne { private ClassTwo classTwo = new ClassTwo(); public synchronized void setClassTwo(int val1, int val2) { classTwo.setVal(val1); classTwo.setVal2(val2); } public s

我试图确保我理解java中同步的性能含义。我有几个简单的类:

public class ClassOne {

    private ClassTwo classTwo = new ClassTwo();

    public synchronized void setClassTwo(int val1, int val2) {
        classTwo.setVal(val1);
        classTwo.setVal2(val2);
    }

    public static void main(String[] args) {
        ClassOne classOne = new ClassOne();
        classOne.setClassTwo(10, 100);
    }

}

public class ClassTwo {

    private int val;
    private int val2;

    public synchronized void setVal(int val) {
        this.val = val;
    }

    public synchronized void setVal2(int val2) {
        this.val2 = val2;
    }

}
因此,正如您在前面的示例中所看到的,我正在同步ClassOne.setClassTwo和ClassTwo.setVal以及ClassTwo.setVal2。我想知道的是,如果我删除ClassTwo.setVal和ClassTwo.setVal2上的同步,性能是否完全相同,如下所示:

public class ClassTwo {

    private int val;
    private int val2;

    public void setVal(int val) {
        this.val = val;
    }

    public void setVal2(int val2) {
        this.val2 = val2;
    }

}

在这个场景中,它们在功能上是等价的(假设没有其他类使用这些类),但是想知道在进行更多的同步时会有多少开销(如果有的话)。

会有开销吗?对

开销会很大吗?视情况而定。
如果只有一个线程,那么答案是“否”,即使在无争用的同步中也很快,据说他们从那时起就做得更好了

那么,如果有多个线程,会发生什么呢?问题是:你发布的两个版本在功能上并不相同。为什么?因为您调用的子方法是公共类的公共方法。因此,它们可以在
setClassTwo
之外调用,因此-没有同步

另外需要注意的是,它们在不同的监视器上同步。第二个版本仅在一个监视器上同步,而原始版本在两个监视器上同步

TL;DR


在需要同步的方法上保留
synchronized
,不要只期望调用方同步(除非它嵌入到类API中)。如果调用方法正确地执行同步,则不会出现争用,开销将非常小,如果它们以某种方式(例如,通过直接调用您的方法)失败,那么您将获得争用和更大的开销,但您仍然具有线程安全性。

在第一种情况下,您可以创建多个线程,并直接在ClassTwo上调用
setVal()
,而不必担心内存不一致(
ClassTwo上的
setVal()
已同步)。在第二种情况下,如果运行多个线程并直接调用
setVal()
,则必须为意外结果做好准备。另外,如果您始终确定
setVal()
将只从
setClassTwo()
调用,那么我建议您使用同步块在
Class2
实例上同步,并保持
setVal()
setVal2()
同步。经验法则-仅同步可同时访问的内容。

同步方式也取决于您计划如何使用Class2。如果写操作很少,而读操作频繁,readwritelock可以在更大范围内为您提供更好的性能。通常,只有在写入过程中,读取才会受阻,因为读取锁由多个读线程共享,而写入会阻塞所有线程,直到写入完成


我希望这能有所帮助。

您有多少个并发线程?为什么不自己试试呢?您可以用Java来测量时间。将它置于重载下,许多线程都会调用它。应该有区别。您不需要同步setVal方法,它应该可以在重载情况下为您节省一些时间。它们在功能上是等效的,-我不这么认为
public synchronized void setClassTwo
-您实际上是在
class1
的实例上进行同步。
setVal()
setVal2()
上的同步关键字是确保其他线程(运行
setClassTwo()
的线程除外)不更新ClassTwo实例的值所必需的。同步
setVal()。请参阅本文,谢谢您的回答。当我说它们“在功能上是等价的”时,我应该加上“在这个场景中”。@dubdot-在这个场景中,只有一个线程:所以,实际上,同步是无用的。(除非考虑锁定/解锁的额外开销)@ DUBDUBDBDOT,很难想象一个情况下,它们除了2个类和一个线程之外,在功能上是等价的。一旦你介绍了一个人,他锁定了
classTwo
对象,甚至连变量都不碰,它们是不同的,就像所有的经验法则一样,有一个相反的方向:同步任何可以并发访问的内容,并处理共享状态,除非您明确确保正确处理并发访问。在本例中,这两个规则之间的冲突突出了另一个小问题-如果一个对象在线程之间共享,则不应允许从其他对象修改所述状态-在该对象内部执行此操作,同时保持适当的同步。@Ordous-well。。非常同意:)