赞
踩
Java并发学习系列文章:Java并发学习-博客专栏
今天在学习极客时间专栏:《Java并发编程实战》
从03 | 互斥锁(上):解决原子性问题
到06 | 用“等待-通知”机制优化循环等待
。
课程主要用银行转账作为解决死锁的例子。
在转账过程中需要2把锁,一个锁锁住转出账户的余额,一把锁锁住转入账户的余额。但加锁的先后顺序会使程序产生死锁。
有以下这四个条件都发生时才会出现死锁:
1.互斥,共享资源 X 和 Y 只能被一个线程占用;
2.占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
3.不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
4.循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
我们选择破坏占有且等待
这个条件使死锁失效。“同时申请”这个操作是一个临界区,我们也需要一个角色(Java 里面的类)来管理这个临界区,我们就把这个角色定为 Allocator。它有两个重要功能,分别是:同时申请资源 apply() 和同时释放资源 free()。账户 Account 类里面持有一个 Allocator 的单例(必须是单例,只能由一个人来分配资源)
现在有了另外一个问题,获取锁失败的线程应该怎么做,常见的做法有:
1.死循环尝试获取锁
2.轮询
3.进入阻塞状态,等待通知。
最好的办法自然是进入阻塞状态,等待通知
。Java提供函数wait
,notify
,notifyAll
。
所以完善后的完整测试代码如下:
import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import static java.lang.System.exit; //相当于临界区,规避死锁 class Allocator { //单例设计模式的饿汉式 static private final Allocator instance = new Allocator(); private Set<Object> res = new HashSet<>(); public static Allocator getInstance() { return instance; } public synchronized void getLock(Object from, Object to) { while (res.contains(from) || res.contains(to)) { try { //将不满足条件的线程阻塞,加入等待的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。