54

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

假设我有这个代码

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作为锁有什么区别?对我来说似乎是一样的。。

当您决定使用同步块时,您如何决定哪个对象成为锁?

4

5 回答 5

66

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

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

于 2010-07-30T07:04:15.163 回答
13

我在阅读 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-synchronized 方法,并且您无法(在您的类中)禁止这种情况在运行时。为避免这种潜在的陷阱,您将在 a 上同步private final ObjectLock使用java.util.concurrent.locks.

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

于 2010-08-02T01:45:14.640 回答
5

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

于 2010-07-30T07:07:22.323 回答
1

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

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

于 2010-07-30T07:09:37.903 回答
0

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

于 2010-07-30T07:05:00.110 回答