java线程同步——条件对象+synchronized 关键字

【0】README

0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java线程同步——条件对象+synchronized 关键字 的相关知识;
0.2)for full source code : https://github.com/pacosonTang/core-java-volume/blob/master/chapter14/SynchBank.java


【1】条件对象

1.1)为什么引入条件对象:通常,线程进入临界区, 却发现在某一条件满足后它才能执行, 要使用一个条件对象来管理那些已经获得了一个锁但是却不能做有用工作的线程;
1.2)看个荔枝:

  • 1.2.1)我们避免选择没有足够资金的账户作为转出账户:
if(bank.getBalance(from) >= amount)
    bank.transfer(from, to, amount)
  • 1.2.2)当前线程有可能在 调用 transfer之前就被CPU切断了
if(bank.getBalance(from) >= amount)
    // thread might be deactived at this point
    bank.transfer(from, to, amount)
  • 1.2.3)我们必须要确保: 没有其他线程在本检查余额 与转账活动间修改余额。通过使用锁保护来支持这一点:
    这里写图片描述

1.3)一个锁对象可以有一个或多个条件对象。 你可以使用 newCondition 方法获得一个条件对象;

  • 1.3.1)如果transfer 发现余额不足,它可以调用:
suffcientFunds.await(); 使得当前线程阻塞了,并放弃锁;(干货——为了避免死锁+await方法的作用)

1.4)等待获得锁的线程和调用 await 方法的线程存在本质的不同:

  • 1.4.1)一旦一个线程调用 await 方法, 它进入该条件的等待集, 当锁可用时,该线程不能马上解除阻塞;
  • 1.4.2)相反,它处于阻塞状态, 直到另一个线程调用同一个条件上的signalAll 方法为止, 这一调用重新激活 因为这一条件而等待的所有线程;
  • 1.4.3)一旦锁成为可用的, 它们中的某个将从 await 方法调用返回, 获得该锁并从阻塞的地方继续执行;

1.5)容易造成死锁:(干货)

  • 1.5.1)原因: 当一个线程调用 await 方法时, 它没有办法重新激活自身。 它寄希望于其他线程,然而,如果最后一个活动线程在解除其他线程的阻塞状态之前就调用了 await 方法, 那么它就 被阻塞了, 这将导致所有的线程都处于阻塞状态,导致死锁;
  • 1.5.2)解决方法:应该何时调用 signalAll 方法呢?经验上讲, 在对象的状态有利于等待线程的方法改变时调用 signalAll方法;

Attention)

  • A1)注意, 调用 signalAll 不会立即激活一个等待线程, 它仅仅解除等待线程的阻塞,以便这些线程可以在当前线程退出同步方法后, 通过竞争实现对对象的访问;(干货——signalAll方法的作用)
  • A2)另一个方法 signal: 则是随机解除等待集中某个线程的阻塞状态,这比解除所有线程的阻塞更加有效, 但也存在危险;(干货——signal 方法的作用)

Warning)

  • W1)当一个线程拥有某个条件的锁时,它仅仅可以在该条件上调用 await, signalAll 或 signal 方法;
  • W2)即是, 一个系统中有多个条件变量(Condition), 每个条件上等待的线程不同, 某条件下的线程只唤醒该条件下的等待进程;
    这里写图片描述

【2】synchronized 关键字

2.1)总结一下有关锁和条件的关键之处:

  • 2.1.1)锁用来保护代码片段, 任何时刻只能有一个线程执行被保护的代码;
  • 2.1.2)锁可以管理试图进入被保护代码段的线程;
  • 2.1.3)锁可以拥有一个或多个相关的条件对象;
  • 2.1.4)每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程;

2.2) Lock 和 Condition 接口专门为 程序设计人员提供了 高度的锁定控制;
2.3)synchronized 关键字: 可用于定义 同步方法和 同步块以保护这个方法或同步块中的代码 在同一时间只能被一个线程访问;
2.4)wait 方法添加一个线程到等待集中, notifyAll/notify 方法解除等待线程的阻塞状态;

  • 2.4.1)换句话说: 调用wait 和 notifyAll 方法 等价于 condition.await() 和 condition.signalAll() 方法;

Annotation)

  • A1) wait, notifyAll , notify 方法是 Object的final方法;
  • A2) await, signalAll, signal 方法 是Condition 接口的方法, 以便与上述方法不会发生冲突;
    这里写图片描述

2.5) 内部锁和条件存在一些局限:

  • 2.5.1)不能中断一个正在试图获得锁的过程;
  • 2.5.2)试图获得锁时不能设定超时;
  • 2.5.3)每个锁仅有单一条件,可能是不够的;

2.6)在代码中应该使用哪一种? Lock 和 Condition 对象还是同步方法?下面是建议:

  • 2.6.1)最好既不用 Lock/Condition 也不用 synchronized关键字; 在许多case 下,你可以使用 java.util.concurrent 包中的一种机制, 它会为你处理所有的加锁。 你会看到如何使用阻塞队列来不同完成一个共同任务的线程; (干货——阻塞队列的作用)
  • 2.6.2)如果 synchronized关键字适合, 请尽量使用它;
  • 2.6.3)如果特别需要 Lock/Condition 结构提供的独有特性, 才使用 Lock/Condition;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值