在线程中使用synchronized

时间:2009-08-30 15:19:17

标签: java keyword synchronized

以下是什么可以理解?
我在SO上经历了this帖子,但仍然无法组装它。

代码1:

synchronized(this){ 
// some code
}

码2:

Object lock = new Object();

synchronized(lock){
// some code
}

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

4 个答案:

答案 0 :(得分:6)

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

当线程到达synchronized(某事)的调用时,它必须在继续之前获取某些内容的锁定。如果你想一次只允许一个线程来修改对象的状态,最明显的事情就是同步该对象的锁定。如果允许并行调用不同的方法,则需要使用不同的锁。

如果您编写synchronized(this)或简单地同步,则线程必须获取与当前对象关联的锁(调用哪种方法)。

请注意,自Java 5.0以来,并发包提供了适当的locks,可以使用它来代替同步。

答案 1 :(得分:3)

将代码放在synchronized块中实际上意味着,“一旦此代码开始运行,其他需要使用此对象的代码就无法同时运行。”

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

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

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

答案 2 :(得分:3)

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

如果封闭类中只有两个同步的块,那么两者之间没有功能的区别,但请考虑这一点:

class CodeOneClass {
  ...
  synchronized(this) {   // or merely "synchronized" - it defaults to this
      first protected code block
  }
  ...
  synchronized(this) {   
      second protected code block
  }
...
}

class CodeTwoClass {
  ...
  Object lock1 = new Object();
  synchronized(lock1) {   
      first protected code block
  }
  ...
  Object lock2 = new Object();
  synchronized(lock2) {   
      second protected code block
  }
...
}

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

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

还有其他差异。一些作家开始指出 synchronized(this)的问题 - 我会在SO上指出你的另一篇文章: Avoid synchronized(this) in Java?

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

答案 3 :(得分: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 tutorial是开始获取并发和锁定信息的好地方。