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

时间:2019-07-24 09:37:45

标签: java multithreading synchronization thread-safety locking

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

代码示例:

Test1.java:

mask = ()

for date_range in date_ranges:
    sub_mask = (df["TimeStamp"] > date_range[0]) & (df["TimeStamp"] < date_range[1])
    mask.append(sub_mask)

df = df.loc[mask]

Test2.java

public class Test1 {

    public void method1() {

        synchronized(CommonUtility.class) {
            SomeObject someObject = testDAO.readSomething();
            .
            .
            .
            testDAO.writeSomething(someObject);
        }
    }
}

要实现此类级别的锁定,请考虑以下示例代码:

public class Test2 {

    public void method2() {

        synchronized(CommonUtility.class) {
            SomeObject someObject = testDAO.readSomething();
            .
            .
            .
            testDAO.writeSomething(someObject);
        }
    }
}

使用Object.class代替CommonUtility.class有什么影响,例如:

synchronized(CommonUtility.class) {
  .
  .
  .
}

3 个答案:

答案 0 :(得分:0)

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

首先,此here建议:

  

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

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

然后:扩大范围(使用Object.class)会使情况变得更糟。

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

答案 1 :(得分:0)

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

写作时,

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

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

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

所谓的“类级别”锁定也是如此:这只是将类本身用作保护其自身静态成员的锁定对象的简便方法。


说到...

优良作法是将锁对象保持在它保护的数据附近(对于“ near”的某种定义)。如果数据为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。这样可以使读者不必检查代码以确保始终将 same 对象用作锁。 (提示:如果您认为需要分配lock变量,那么您所做的事情可能超出许多程序员的舒适度,或者犯了一个严重的错误。)

答案 2 :(得分:0)

  

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

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

也许像这样:

public class XmlHelper {
    public XmlHelper(File xmlFile) {
    public synchronized SomeObject readSomething() { ... }
    public synchronized void writeSomething(SomeObject someObject) { ... }
}

然后,您的Test1Test2类必须共享XmlHelper类的相同实例,因此它们的锁将相互阻塞。实例级锁并不总是一个好主意,因为应该尽可能地细化锁,但是在您的应用程序中这很好,因为XmlHelper是为多个类设计的,用于锁定其IO操作。

  

使用Object.class代替CommonUtility.class有什么影响,例如:

正如其他人所提到的,对类的锁定与调用synchronized static方法相同。由于锁的纹理非常粗糙,因此应非常谨慎地使用此模式。如果您的程序需要同时读取/写入2个XML文件怎么办?您的类级别锁定将导致IO操作对这两个文件相互阻塞-并非最佳。

如果您锁定了Object.class,那么任何其他正在执行相同锁定的类都将不必要地阻塞您的线程。上帝帮助你。

  

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

有两种方法可以做到这一点。您的XmlHelper类上可能有某种更新方法:

public synchronized void updateObject(...) {
   SomeObjecto obj = readSomething();
   // adjust object here based on the arguments
   writeSomething(obj);
}

如果每个线程需要执行自己的更新,则它们将需要在外部锁定同一对象。我建议锁定XmlHelper的共享实例。

synchronized (xmlHelper) {
     ...
}

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