为了账号安全,请及时绑定邮箱和手机立即绑定

在同步块中使用 Object.class 锁有什么影响?

在同步块中使用 Object.class 锁有什么影响?

慕桂英4014372 2023-04-19 15:50:09
我正在尝试从多个类中的多个方法同步对 xml 文件的读写操作。为此,我使用类级锁来同步此操作。代码示例:Test1.java:public class Test1 {    public void method1() {        synchronized(CommonUtility.class) {            SomeObject someObject = testDAO.readSomething();            .            .            .            testDAO.writeSomething(someObject);        }    }}Test2.javapublic class Test2 {    public void method2() {        synchronized(CommonUtility.class) {            SomeObject someObject = testDAO.readSomething();            .            .            .            testDAO.writeSomething(someObject);        }    }}要实现此类级别的锁定,请考虑以下示例代码:synchronized(CommonUtility.class) {  .  .  .}使用 Object.class 而不是 CommonUtility.class 有什么影响,例如:synchronized(Object.class) {  .  .  .}
查看完整描述

3 回答

?
森林海

TA贡献2011条经验 获得超2个赞

我正在尝试从多个类中的多个方法同步对 XML 文件的读写操作。为此,我使用类级锁来同步此操作。


那不是一个好主意。您应该有一个类(可能是XmlHelper)来管理 XML 文件并进行锁定。 XmlHelper将在多个类的多个方法中使用,并将控制对文件的锁定,而不是多个类必须担心它。这是更好的对象设计。


也许是这样的:


public class XmlHelper {

    public XmlHelper(File xmlFile) {

    public synchronized SomeObject readSomething() { ... }

    public synchronized void writeSomething(SomeObject someObject) { ... }

}

然后你的Test1和Test2类必须共享同一个XmlHelper类的实例,这样他们的锁就会互相阻塞。实例级锁并不总是一个好主意,因为锁应该尽可能细粒度,但它在您的应用程序中很好,因为它是XmlHelper为多个类设计的,可以锁定它们的 IO 操作。


Object.class使用instead of有什么影响CommonUtility.class,例如:


正如其他人所提到的,锁定类与调用方法相同synchronized static。这种模式应该非常谨慎地使用,因为锁的粒度太粗了。如果您的程序需要同时读取/写入 2 个 XML 文件怎么办?您的类级别锁会导致对 2 个文件的 IO 操作相互阻塞——这不是最佳的。


如果您锁定,Object.class那么碰巧执行相同锁定的任何其他类都会不必要地阻塞您的线程。愿上帝保佑你。


竞争条件正在受到打击。例如:thread1 读取文件内容并更新读取的内容。在线程 1 写回文件之前,线程 2 读取内容。然后 thread1 将更新的内容写入文件。最后 thread2 将内容写入文件。这导致内容丢失,


有几种方法可以做到这一点。你可以在你的类上有某种更新方法XmlHelper:


public synchronized void updateObject(...) {

   SomeObjecto obj = readSomething();

   // adjust object here based on the arguments

   writeSomething(obj);

}

如果每个线程都需要进行自己的更新,那么它们将需要在同一个对象上进行外部锁定。我建议锁定XmlHelper.


synchronized (xmlHelper) {

     ...

}

类对象上的锁定将起作用,但建议使用大锤子。同样,如果您有 2 个实例XmlHelper处理 2 个不同的文件,您不希望 2 个不同文件上的 IO 操作相互阻塞。


查看完整回答
反对 回复 2023-04-19
?
泛舟湖上清波郎朗

TA贡献1818条经验 获得超3个赞

我认为这两种方法都不理想。

首先,这里建议:

您可能想知道调用静态同步方法时会发生什么,因为静态方法与类而不是对象相关联。在这种情况下,线程获取与该类关联的 Class 对象的内部锁。

换句话说:当您使用时,synchronized(CommonUtility.class)隐式地与 CommonUtility 中的所有静态方法“同步” synchronized。更糟糕的是:想象一下今天的类没有这样的方法。但是下周,有人synchronized在该实用程序类中添加了这样一个静态方法,假设只有对该方法的调用通过该监视器。最坏的情况是,这可能会导致一些丑陋的(仅限运行时)意外。

然后:寻求更广泛的范围(通过使用 Object.class)会使事情变得更糟。

我的回答:首先避免使用类对象。


查看完整回答
反对 回复 2023-04-19
?
Qyouu

TA贡献1786条经验 获得超11个赞

IMO,“类级”锁和“对象级”锁的想法让人分心。Java 中只有一种底层同步机制:synchronized (o) { ... }whereo可以是任何 Java 对象(注意在 Java 中MyClass.class 是一个对象。)


当你写作时,


synchronized SomeType foobar(...) { ... }

这实际上只是一种使用实例作为保护其自身成员变量的锁对象的快捷方式。


SomeType foobar(...) {

    synchronized (this) {

        ...

    }

}

所谓的“类级别”锁定也是如此:它只是使用类本身作为保护其自身静态成员的锁定对象的一种简写方式。


说到哪...


好的做法是将锁定对象保持在它保护的数据附近(对于“附近”的某些定义)。如果数据是private,那么锁定对象应该是private。如果数据是某个实例的成员,那么锁对象应该是同一实例的成员,等等。


Object.class不是特别“接近”任何东西。它可以像任何其他对象一样工作,但是使用它会使您的代码更难理解,因为读者会花时间想知道是什么促使您选择它Object.class,并想知道您的选择是否基于误解。


我自己的偏好,为了保护实例成员,看起来像这样:


class MyClass {

    private final Object lock = new Object();

    private SomeType someVariableThatNeedsProtection = ...;

    private SomeOtherType someOtherVariableThatAlsoNeedsProtection...;

    ...

    public ... foobar(...) {

        synchronized(lock) {

            ...

        }

    }

}

而且,如果我需要保护static成员:


...

private static final Object lock = new Object();

...

变量lock是private,就像它保护的数据一样。想要了解您的代码的任何人都不需要花费任何时间来搜索受同一锁对象保护的任何其他内容,因为他们知道无法从方法外部访问这些内容MyClass。


变量lock也是final。这将使读者不必检查您的代码以确保它始终是用作锁的同一对象。(提示:如果你认为你需要能够分配lock变量,那么你要么在做一些超出许多程序员舒适水平的复杂事情,要么你正在犯一个严重的错误。)


查看完整回答
反对 回复 2023-04-19
  • 3 回答
  • 0 关注
  • 135 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信