【Java并发】JAVA并发编程实战-读书笔记16
来源:互联网 发布:注册中文域名有价值吗 编辑:程序博客网 时间:2024/06/04 00:51
Java应用程序不能从死锁中恢复,所以确保你的设计能够避免死锁出现的先决条件是非常有价值的。
public class LeftRightDeadLock(){ private final Object left=new Object(); private final Object right=new Object(); public void leftRight(){ synchronized(left){ synchronized(right){ doSomeThing(); } } } public void rightLeft(){ synchronized(right){ synchronized(right){ doSomethingElse(); } } }}
上面的程序很容易发生死锁。
如果所有线程以通用的固定顺序获得锁就不会出现顺序死锁的问题。
public void transferMoney(Account fromAccount,Account toAccount DollarAmount amount) throws InsufficientFundsException{ synchronized(fromAccount){ synchronized(toAccount){ if(fromAccount.getBalance().compareTo(amount)<0){ throw new InsufficientFundsException(); }else{ fromAccount.debit(amount); toAccount.credit(amount); } } }}
上面的程序易导致死锁,原因:如果一个x到y转账和y到x转账同时发生,就会出现获得锁,等待对方锁的状态。
private static final Object tieLock=new Object();public void transferMoney(final Account fromAccount,final Account toAccount,final DollarAmount amount)throws InsufficientFundsException{ class Helper{ public void transfer()throws InsufficientFundsException{ if(fromAccount.getBalance().compareTo(amount)>0){ throw new InsufficientFundesException(); }else{ fromAccount.debit(amount); toAccount.credit(amount); } } } int fromHash=System.identityHashCode(fromAcct); int toHash=System.indentityHashCode(toAcct); if(fromHash<toHash){ synchronized(fromAcct){ synchronized(toAcct){ new Helper().transfer(); } } }else if(fromHash>toHash){ synchronized(toAcct){ synchronized(fromAcct){ new Helper.transfer(); } } }else{ synchronized(tieLock){ synchronized(fromAcct){ synchronized(toAcct){ new Helper.transfer(); } } } }}
两个对象hashCode可能会有相同的数值。但是概率较低。
另外经常出现哈希冲突,那么这个技术可能会成为并发性的瓶颈。
public class DemonstrateDeadLock{ private static final int NUM_THREADS=20; private static final int NUM_ACCOUNTS=5; private static final int NUM_ITERATIONS=1000000; public static void main(String[] args){ final Random rnd=new Random(); final Account[] accounts=new Account[NUM_ACCOUNTS]; for(int i=0;i<accounts.length;i++){ accounts[i]=new Account(); } class TransferThread extends Thread{ public void run(){ for(int i=0;i<NUM_ITERATIONS;i++){ int fromAcct=rnd.nextInt(NUM_ACCOUNTS); int toAcct=rnd.nextInt(NUM_ACCOUNT); DollarAmount amount=new DollarAmount(rnd.nextInt(1000)); transferMoney(accounts[fromAcct], accounts[toAcct],amount); } } } for(int i=0;i<NUM_THREADS;i++){ new TransferThread().start(); } }}
上面的例子在大多数系统下都会很快发生死锁。
class Taxi{ private Point location,destination; private final Dispatcher dispatcher; public Taxi(Dispatcher dispatcher){ this.dispatcher=dispatcher; } public synchronized Point getLocation(){ return location; } public synchronized void setLocation(Point location){ this.location=location; if(location.equals(destination)){ dispatche.notifyAvailable(this); } }}class Dispatcher{ private final Set<Taxi> taxis; private final Set<Taxi> availableTaxis; public Dispatcher(){ taxis=new HashSet<Taxi>(); availableTaxis=new HashSet<Taxi>(); } public synchronized void notifyAvailable(Taxi taxi){ availableTaxis.add(taxi); } public synchronized Image getImage(){ Image image=new Image(); for(Taxi t:taxis){ image.drawMarker(t.getLocation()); } return image; }}
上面的例子会造成死锁。setLocatioin方法和getImage方法存在被两个线程以不同的顺序占有的可能。
在持有一个锁后,调用一个外部方法是很危险的。
class Taxi{ private Point location,destination; private final Dispatcher dispatcher; public synchronized Point getLocation(){ return location; } public synchronized void setLocation(Point location){ booelan reachedDestination; synchronized(this){ this.location=location; reachedDestination=location.equals(destination); } if(reachedDestination){ dispatcher.notifyAvailable(this); } }}class Dispatcher{ private final Set<Taxi> taxis; private final Set<Taxi> availableTaxis; public synchronized void notifyAvailable(Taxi taxi){ availableTaxis.add(taxi); } public Image getImage(){ Set<Taxi> copy; synchronized(this){ copy=new HashSet<Taxi>(taxis); } Image image=new Image(); for(Taxi t:copy){ image.drawMarker(t.getLocation()); } return image; }}
持有定时锁可以一定程度的避免死锁,但是只有同时获得两个锁的时候才有效,如果多个锁是在嵌套的方法中被请求的,你无法仅仅释放外层的锁,尽管你知道自己已经持有锁。
活锁是线程中活跃度失败的另一种形式,尽管没有被阻塞,线程仍不能继续,因为他不断重试相同的操作,总是失败。活锁通常发生在消息处理应用中,将处理失败的消息回退回队列重新处理,这种形式的活锁通常来源于过度的错误恢复代码,误将不可修复的错误当做是可修复的错误。
活锁同样可以发生在多个线程的协作间,为了彼此间响应而修改了状态,使得没有一个线程能够继续前进。好比两个人在半路相遇,由于互相让路导致他们在另一条路上相遇,一直避让下去。
解决活锁的一种方案就是对重试机制引入一些随机性。比如以太网上两个基站间数据包冲突,如果都非常精确的休息一秒后重发会再次冲突,这是可以随机等待,然后重新发送。通过随机等待和撤回重试能够相当有效的避免活锁的发生。
可伸缩性指的是:当增加计算资源的时候,吞吐量和生产量能够相应地得以改进。
如果F是必须串行执行的比重,那么Amdahl定律告诉我们,在一个N处理器的机器中。
speedup<=1/(F+(1-F)/N)
当N趋近于无限大时,speedup最大值无限接近于1/F。
所有的并发程序都有一些串行源。
有两个原因影响着锁的竞争性:锁被请求的频率、每次持有该锁的时间。如果这两者的乘积足够小,那么大多数请求锁的尝试都是非竞争的,这样竞争性的锁将不会成为可伸缩性的障碍。
- 《Java并发编程实战》读书笔记
- 《Java并发编程实战》读书笔记
- java并发编程实战-读书笔记
- 《Java并发编程实战》读书笔记
- 《java并发编程实战》读书笔记
- 《Java并发编程实战》读书笔记
- 读书笔记-《Java并发编程实战》
- java并发编程实战读书笔记
- java并发编程实战读书笔记
- Java并发编程实战读书笔记
- 【Java并发】JAVA并发编程实战-读书笔记16
- java并发实战读书笔记
- 【Java并发】JAVA并发编程实战-读书笔记1
- 【Java并发】JAVA并发编程实战-读书笔记2
- 【Java并发】JAVA并发编程实战-读书笔记3
- 【Java并发】JAVA并发编程实战-读书笔记4
- 【Java并发】JAVA并发编程实战-读书笔记5
- 【Java并发】JAVA并发编程实战-读书笔记6
- 51nod 1276 岛屿的数量
- POJ 1298 史上最难的问题 3749 破译密码 G++
- C专家编程 五 声明的优先级规则
- ibatis 到 MyBatis区别
- Ubuntu 查看CPU/GPU内存使用情况
- 【Java并发】JAVA并发编程实战-读书笔记16
- 51nod 1278 相离的圆
- 洛谷 P1136 迎接仪式
- 51nod 1279 扔盘子
- shell, 文字游戏,在一个文本文件中查找出每个单词的使用频率并排序
- stack栈的用法
- 说说java 中 static 的基础知识和易混点
- 51nod 1283 最小周长
- JDK容器并发IO经典blog