lock对同步和使用它作为锁有什么区别?

时间:2010-07-30 07:00:22

标签: java concurrency locking synchronized synchronized-block

我知道同步方法和同步块之间的区别,但我不确定同步块部分。

假设我有这段代码

class Test {
  private int x=0;
  private Object lockObject = new Object();

  public void incBlock() {
    synchronized(lockObject) {
      x++;
    }
    System.out.println("x="+x);
  }

  public void incThis() {  // same as synchronized method
    synchronized(this) {
      x++;
    }
    System.out.println("x="+x);
  }
}

在这种情况下,使用 lockObject 和使用 this 作为锁定有什么区别?对我来说似乎是一样的..

当您决定使用synchronized块时,如何确定哪个对象是锁?

5 个答案:

答案 0 :(得分:62)

就我个人而言,我几乎从不锁定“这个”。我通常会锁定一个私有的引用,我知道没有其他代码可以锁定。如果您锁定“this”,那么任何其他知道您的对象的代码可能会选择锁定它。虽然它不太可能发生,但肯定会发生 - 并且可能导致死锁,或者只是过度锁定。

关于你锁定的东西并没有什么特别神奇的东西 - 你可以有效地将它视为一种象征。任何使用相同令牌锁定的人都将尝试获取相同的锁。除非您希望其他代码能够获取相同的锁,否则请使用私有变量。我鼓励你制作变量final - 我不记得我希望更改锁定变量的情况对象的生命周期。

答案 1 :(得分:11)

当我阅读Java Concurrency In Practice时,我有同样的问题,我想我会在Jon Skeet和spullara提供的答案上添加一些额外的观点。

这是一些示例代码,它会在setValue(int)方法执行时阻止“快速”getValue() / doStuff(ValueHolder)方法。

public class ValueHolder {
    private int value = 0;

    public synchronized void setValue(int v) {
        // Or could use a sychronized(this) block...
        this.value = 0;
    }

    public synchronized int getValue() {
        return this.value;
    }
}

public class MaliciousClass {

    public void doStuff(ValueHolder holder) {
        synchronized(holder) {
            // Do something "expensive" so setter/getter calls are blocked
        }
    }
}

使用this进行同步的缺点是其他类可以同步对类的引用(当然不是通过this)。在锁定对象引用时恶意或无意使用synchronized关键字会导致您的类在并发使用情况下表现不佳,因为外部类可以有效地阻止您的this - 同步方法,而您没有任何关系可以(在你的班级中)在运行时禁止这个。为了避免这种潜在的陷阱,您可以在private final Object上同步或使用Lock中的java.util.concurrent.locks界面。

对于这个简单的例子,您可以使用AtomicInteger而不是同步setter / getter。

答案 2 :(得分:5)

Effective Java Second Edition的第67项是避免过度同步,因此我会在私有锁对象上进行同步。

答案 3 :(得分:1)

Java中的每个对象都可以充当监视器。选择一个取决于您想要的粒度。选择'this'的优点和缺点是其他类也可以在同一个监视器上同步。我的建议是避免直接使用synchronize关键字,而是使用java.util.concurrency库中更高级别且具有良好定义语义的构造。这本书从非常着名的专家那里得到了许多很好的建议:

实践中的Java并发 http://amzn.com/0321349601

答案 4 :(得分:0)

在这种情况下,选择锁定哪个对象并不重要。但是必须始终使用相同的对象进行锁定才能实现正确的同步。上面的代码不能确保正确的同步,因为你曾经使用'this'对象作为锁,然后将'lockObject'作为锁。