您的位置:首页 > 编程语言 > Java开发

java线程同步——竞争条件的荔枝+锁对象

2015-12-20 21:32 381 查看

【0】README

0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java线程同步——竞争条件的荔枝+锁对象 的相关知识;

0.2) for full source code, please visit https://github.com/pacosonTang/core-java-volume/blob/master/chapter14/ThreadFive.java

【1】竞争条件的荔枝

1.1)某银行有若干个账户(我们这里设置为4个),且初始余额均为10000; 账户间存在着转账操作;

Attention):在模拟这个程序的过程中, 不清楚在某一时刻某银行的账户转账多少钱,但是其总金额应该是不变的, 因为我们所做的一切不过是从银行的一个账户转账到另一个账户(干货——无论怎样转账,总金额应该是不变的,始终为40000)。



对上述运行结果的分析(Analysis):

A1)问题在于这不是一个原子操作, 该指令可能被如下处理:

step1)将 account[to] 加载到寄存器;

step2)增加 amount 到 account[to]上;

step3)将结果写回 account[to];

A2)现在线程1 执行step1 + step2, 然后CPU切换到执行线程2;

A2.1)假设线程2修改了account[to]的值;

A2.2)之后,线程1被唤醒,然后进行 step3;

A3)那显然, 线程1的step3操作重写了 线程2的写入结果(线程1擦除线程2所做的更新), 所以就出现了最终的总金额不等于 40000的错误结果;

A4)如果要保持结果的合理性,只需要达到一个目的,就是将对账户余额的访问加以限制,每次只能有一个线程在访问。这样就能保证账户中余额数据的合理性了。

1.2)解决方法:

1.2.1)使用同步块(synchronized):在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:(干货——无论怎样转账,总金额应该是不变的,始终为40000)。



1.2.2)使用同步方法:(干货——无论怎样转账,总金额应该是不变的,始终为40000)。



【2】锁对象

2.1)有两种方法防止代码块受并发访问的干扰:

2.1.1) synchronized 关键字, 构造同步块,或是同步方法;(如1.2中解决方法 所示)

2.1.2) java 5.0 引入的 ReentrantLock 类;

2.2)用ReentrantLock保护代码块的基本结构

myLock.lock()
try
{}
finally
{
myLock.unlock()
}


2.2.1)这一结构确保了 任何时刻只有一个线程进入临界区域, 一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。 当其他线程调用 lock 时, 它们会被阻塞,直到第一个线程释放锁对象;

Warning)把解锁操作包括在了 finally 子句中是至关重要的。 如果在临界区的代码抛出异常, 锁必须被释放, 否则, 其他线程将永远阻塞下去;



Attention)

A1)锁是可重入的, 因为线程可以重复地获得已持有的锁;

A2)锁保持一个持有计数来跟踪对 lock 方法的嵌套调用, 线程在每一次调用lock 都要调用 unlock 来释放锁;

A3)由于这一特性, 被一个锁保护起来的代码可以调用另一个使用相同的锁的方法;

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java