3

例如,我有一个带有 2 个计数器的类(在多线程环境中):

public class MyClass {
  private int counter1;
  private int counter2;

  public synchronized void increment1() {
        counter1++;
  }

  public synchronized void increment2() {
        counter2++;
  }

}

有 2 个彼此不相关的增量操作。但我使用相同的对象锁定(this)。

确实,如果客户端同时调用increment1()increment2()方法,那么increment2调用将被阻塞,直到increment1()释放this监视器?

如果是真的,是否意味着我需要为每个操作提供不同的监视器锁(出于性能原因)?

4

7 回答 7

12

确实,如果客户端同时调用 increment1() 和 increment2() 方法,那么 increment2 调用将被阻塞,直到 increment1() 释放 this 监视器?

如果他们在同一个实例上被调用,那么是的。

如果是真的,是否意味着我需要为每个操作提供不同的监视器锁(出于性能原因)?

只有你能知道。我们不知道您的性能要求。这实际上是您真实代码中的问题吗?您的实际操作是否持久?它们是否经常发生?您是否进行了任何诊断以估计其影响?您是否对您的应用程序进行了概要分析,以了解等待监视器花费了多少时间,更不用说什么时候没有必要了?

我实际上建议不要this出于完全不同的原因进行同步。当您确实控制一切时,已经很难推理线程- 但是当您不知道可以获取监视器的所有内容时,您就会一无所获。当您在 上同步时this,这意味着任何其他引用您的对象的代码也可以在同一监视器上同步。例如,客户可以使用:

synchronized (myClass) {
    // Do something entirely different
}

这可能会导致死锁、性能问题以及各种各样的事情。

如果你private final在你的类中使用一个字段,而创建一个对象只是为了作为一个监视器,那么你知道获取该监视器的唯一代码将是你的代码。

于 2013-01-29T07:19:59.747 回答
5

1) 是的,increment1() 确实会阻止 increment2(),反之亦然,因为它们都隐式同步this

2) 如果您需要更好的性能,请考虑无锁 java.util.concurrent.atomic.AtomicInteger 类

  private AtomicInteger counter1 = new AtomicInteger();
  private AtomicInteger counter2 = new AtomicInteger();

  public void increment1() {
        counter1.getAndIncrement();
  }

  public void increment2() {
        counter2.getAndIncrement();
  }
于 2013-01-29T07:21:28.257 回答
2

如果您对方法进行同步,就像您在此处所做的那样,您将锁定整个对象,因此从同一个对象访问不同变量的两个线程无论如何都会相互阻塞。

如果你想一次只同步一个计数器,这样两个线程在访问不同变量时不会互相阻塞,你必须在两个同步块中添加两个计数器,并使用不同的变量作为两者的“锁”块。

于 2013-01-29T07:19:03.097 回答
2

你是对的,如果你使用相同的对象,这将是一个性能瓶颈。您可以为单个计数器使用不同的锁或java.util.concurrent.atomic.AtomicInteger用于并发计数器。

像:

public class Counter {
  private AtomicInteger count = new AtomicInteger(0);
  public void incrementCount() {
    count.incrementAndGet();
  }
  public int getCount() {
    return count.get();
  }
}
于 2013-01-29T07:20:54.370 回答
1

是的,如果多个线程尝试在您的对象上调用方法,它们将等待尝试获取锁(尽管不能保证获得锁的顺序。)与所有事情一样,在您知道这是瓶子之前没有理由进行优化缩在你的代码中。

于 2013-01-29T07:20:43.097 回答
1

是的,给定的代码与以下代码相同:

  public void increment1() {
        synchronized(this) {
             counter1++;
        }
  }

  public oid increment2() {
        synchronized(this) {
             counter2++;
        }
  }

这意味着只能同时执行一种方法。您应该提供不同的锁(this一开始就锁定是一个坏主意),或者其他一些解决方案。第二个是你真正想要的:AtomicInteger

于 2013-01-29T07:21:19.660 回答
0

如果您需要能够并行调用这两个操作所带来的性能优势,那么可以,您不需要为不同的操作提供不同的监视器对象。

但是,对于过早的优化有一些话要说,在使程序更复杂以适应它之前,您应该确保需要它。

于 2013-01-29T07:19:39.087 回答