1

对以下内容的理解是什么?
我已经在 SO 浏览过这篇文章,但仍然无法组装它。

代码1:

synchronized(this){ 
// some code
}

代码2:

Object lock = new Object();

synchronized(lock){
// some code
}

任何教程,或一些解释同步的链接,就像他们向孩子解释一样?

4

4 回答 4

6

基本上,Java 中的每个对象都有一个“锁”。

当一个线程调用 synchronized(something) 时,它必须在继续之前获取某物的锁。如果你想一次只允许一个线程修改一个对象的状态,最明显的就是同步那个对象的锁。如果允许并行调用不同的方法,则需要不同的锁。

如果你写了synchronized(this),或者简单的synchronized,线程必须获取与当前对象(调用哪个方法)相关的锁。

请注意,从 Java 5.0 开始,并发包提供了适当的,可以用来代替同步。

于 2009-08-30T15:23:23.730 回答
3

将代码放在一个synchronized块中实质上意味着“一旦该代码开始运行,其他需要使用该对象的代码就不能同时运行”。

因此,如果线程#2 正在您的code2块中执行代码,那么当涉及到synchronized(lock)代码时,它必须有效地环顾所有其他线程,以确保目前没有其他人正在与lock对象运行“同步”代码。线程#1 肯定同时运行一些代码,但它可能是完全不相关的代码。如果是这样,线程#2 可以安全地开始运行你的 " some code" 东西。

同时,如果 Thread #1 到达synchronized(this)块,它也必须暂停并查看是否有其他线程正在使用this. 如果this和 是同一个对象lock,我们就有问题了。我们被告知只有一个线程可以同时使用该对象(在同步块中)。然而 Thread #2 已经在使用它了。线程#1 只需要等待……等待……再等待……直到线程#2 最终完成。然后我们可以继续。

最终结果是一次synchronized只能运行一个块(当然,对于特定对象)。

于 2009-08-30T15:29:30.707 回答
3

在已经给出的其他出色答案中没有提到的一件事是code1和code2之间的区别。在代码 1 中,同步在找到代码的对象的实例上,而在代码 2 中,同步在对象内的特定锁定对象上。

如果封闭类中只有两个同步块,则两者之间没有功能差异,但考虑一下:

类 CodeOneClass {
  ...
  synchronized(this) { // 或仅仅是“同步” - 默认为 this
      第一个受保护的代码块
  }
  ...
  同步(这个){   
      第二个受保护的代码块
  }
...
}

类 CodeTwoClass {
  ...
  对象锁1 =新对象();
  同步(锁1){   
      第一个受保护的代码块
  }
  ...
  对象锁2 =新对象();
  同步(锁2){   
      第二个受保护的代码块
  }
...
}

如果两个线程试图使用相同的 CodeOneClass 实例,则只有一个线程可以同时位于两个受保护的代码块中的任何一个中。

但是对于第二个习惯用法,您可以灵活地说一个线程位于第一个受保护块中,另一个线程位于另一个中是安全的。请注意,如果锁相同(都在同一个锁对象上同步),则行为将与第一个相同。

还有其他区别。一些作者开始指出synchronized(this)的问题——我会指出你在 SO 上的另一篇文章: 避免 Java 中的 synchronized(this)?

我强烈建议阅读它,以及它链接到的三个帖子。

于 2009-08-30T16:16:26.807 回答
2

假设您有一个Account具有方法的对象:

void debit(long debitAmount, Account beneficiary) throws InsufficientFundsException
{
   if (accountBalance >= debitAmount) {
      accountBalance -= debitAmount;
      beneficiary.credit(debitAmount);
   }
   else {
      throw new InsufficientFundsException();
   }
}

现在假设您有一个余额为 100 欧元的帐户,并且您有两次尝试从其借记 70 欧元。如果两个借记同时发生,您可以获得如下竞争条件

  • 第一次借记检查账户余额:100 >= 70,所以成功
  • 第二次借记检查账户余额:100 >= 70,所以成功
  • 第一次借记执行;账户余额变为30
  • 执行第二次借记;账户余额变为-40。不应该被允许

Account我们可以通过同步对象的锁来防止这种可怕的情况:

void debit(long debitAmount, Account beneficiary) throws InsufficientFundsException
{
   synchronized (this) {
      if (accountBalance >= debitAmount) {
         accountBalance -= debitAmount;
         beneficiary.credit(debitAmount);
      }
      else {
         throw new InsufficientFundsException();
      }
   }
}

这样可以确保帐户余额和借方的测试不会被帐户余额的另一项测试中断。

Sun Java 教程是了解并发和锁定信息的好地方。

于 2009-08-30T15:35:44.563 回答