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对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树