【问题标题】:using synchronized in Threads在线程中使用同步
【发布时间】:2009-08-30 15:19:17
【问题描述】:

下面的理解是什么?
我已经浏览了 SO 的 this 帖子,但仍然无法组装它。

代码1:

synchronized(this){ 
// some code
}

代码2:

Object lock = new Object();

synchronized(lock){
// some code
}

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

【问题讨论】:

    标签: java keyword synchronized


    【解决方案1】:

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

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

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

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

    【讨论】:

    【解决方案2】:

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

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

    类 CodeOneClass { ... synchronized(this) { // 或仅仅是“同步” - 默认为 this 第一个受保护的代码块 } ... 同步(这个){ 第二个受保护的代码块 } ... } 类 CodeTwoClass { ... 对象锁1 =新对象(); 同步(锁1){ 第一个受保护的代码块 } ... 对象锁2 =新对象(); 同步(锁2){ 第二个受保护的代码块 } ... }

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

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

    还有其他区别。一些作者开始指出 synchronized(this) 的问题 - 我会向您指出关于 SO 的另一篇文章: Avoid synchronized(this) in Java?

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

    【讨论】:

    • 其他答案也很好?希望我可以选择标记更多正确/有用的答案吗?
    【解决方案3】:

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

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

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

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

    【讨论】:

      【解决方案4】:

      假设你有一个 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 是了解并发和锁定信息的好地方。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-27
        • 2015-09-23
        • 2015-08-13
        • 2012-02-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多