为什么将伪对象作为块级同步的参数传递?

时间:2012-02-01 12:41:56

标签: java multithreading synchronized

在Spring源代码中找到此代码。这是将XML文件转换为Bean Tree的第一步。

/** Synchronization monitor for the "refresh" and "destroy" */
    private final Object startupShutdownMonitor = new Object();

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
}

6 个答案:

答案 0 :(得分:7)

这个习惯用于更精细的同步。以下是Java tutorial的摘录。您可以使用 synchronized(this) ,但这已经锁定了整个对象

  

同步语句对于提高并发性也很有用   细粒度的同步。例如,假设MsLunch类有   两个实例字段,c1和c2,从不一起使用。所有   必须同步这些字段的更新,但没有理由   防止c1的更新与c2的更新交错 -   这样做可以通过创建不必要的阻塞来减少并发性。   而不是使用同步方法或以其他方式使用锁   与此相关,我们创建两个对象仅用于提供锁定。

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

启动Java 5,java引入了Lock抽象提供了更多功能。因此,您可以执行类似下面的操作,而不是synchronized(obj)。阅读更多details here

Lock lock = new ReentrantLock();

lock.lock();

c1++;

lock.unlock();

答案 1 :(得分:1)

我不能专门为春季作者发言,但总的来说......

这样做是为了使代码同步(显然),但故意不在this上同步。

我们为什么要这样做?有几个原因

  1. 类中还有其他同步块,但这些块与此块之间没有相互依赖关系。因此,它们不应围绕同一个锁同步 - 每个资源围绕不同的对象进行同步。
  2. 您想隐藏您班级用户的锁定。您的某个用户可能会决定执行synchronized(theObject)并最终使用同一个对象作为您班级内部锁定的同一个对象。在某些情况下,这可能会导致严重的性能/并发问题
  3. 为何选择Object?因为这就是获得锁定所需的全部内容,而其他任何东西都会带来更多的开销。

答案 2 :(得分:1)

我认为从根本上你会想知道他们为什么不使用synchronized(this)?您发布的代码可能更好的原因有很多。

  1. 安全(或更确切地说,是行为的确定性)。由于其他类可能引用了类的实例,因此它们也可以自由地同步它。 (事实上​​,每次同步this时,你几乎肯定会在另一个类的监视器上进行同步)。您可以编写代码,使其独立运行,但如果其他代码使用它,则会以特定方式进行同步,从而创建死锁。 this上的同步实际上是公开的,但我怀疑你通常会记录它(或者另一个开发人员会阅读该文档)。通过在这样的私有final字段上进行同步,可以保证没有其他代码可以在同一个对象上同步,从而大大简化了需要保护的逻辑。
  2. 同一班级内的多项操作。例如,如果您有两个单独的计数器并且正在使用同步来防止丢失更新 - 那么写入计数器1就没有理由阻止读取计数器2.如果所有this上同步那么这些方法中只有一种可以同时发生。通过在特定对象上进行同步,您可以创建在组内互斥的方法/块组,但不会阻止来自其他组的块继续进行。
  3. 第一个可以说是最重要的,因为它可以设置一个非常难以调试的定时炸弹。第二个也是相关的 - 开发人员经常过度约束这样的独立操作而没有意识到 - 但仅适用于同一类中有多组操作的情况。

    现在,如果我正在进行同步,我将始终在特定的new Object()字段上进行,即使它目前只是一个操作。

答案 3 :(得分:0)

啊我以前见过这个:)我实际上在考虑同样的事情。

如果我没记错的话,这是来自AbstractApplicationContext。

将方法标记为已同步意味着没有2个线程可以同时调用此方法或任何其他同步方法(锁定在类上)。

拥有一个监视器对象并在这样的同步块中使用它具有相同的效果,但是对于使用相同虚拟对象的同步块中的所有代码。

如果我没记错的话,在同一个类中有另一个名为activeMonitor的监视器。

这允许更高的并发性(线程访问同时由两个对象同步的方法)。

这样可以防止他将他的显示器(这是他班级以外的任何人都不感兴趣)泄露给可能滥用它的其他物体(将其置于永恒的dib状态),从而弄乱他的过程

答案 4 :(得分:0)

如果一段具有某些类变量的代码完全独立于另一个,那么不是通过使用synchronize(this)获取整个对象的锁定,而是最好对虚拟对象使用锁定。内部的代码仍然受到保护,因为监视器现在位于虚拟对象锁定。

答案 5 :(得分:-1)

private final Object startupShutdownMonitor = new Object();

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
       // Prepare this context for refreshing.
       prepareRefresh();
   }

}

这相当于以下

public void refresh() throws BeansException, IllegalStateException {
      //Do something that does not affect the state of the object
      System.out.println("I am inside the refresh method() and will accquire lock on the object now");
      prepareRefresh();
}

private synchronized void preparedRefresh() {
    //Do something thread safe here
    //Since the thread here has the monitor it can safely alter the state of the class instance here with causing inconsistensy
}

获取实例对象startupShutdownMonitor的锁定与获取正在调用刷新方法的类实例上的锁定相同。