java编程思想之并发(死锁)
一个对象可以有 synchronized 方法或其他形式的加锁机制来防止别的任务在互斥还没有释放的时候就访问这个对象。
死锁
任务有可能变成阻塞状态,所以就可能发生这样的情况:某个任务在等待另一个任务,而后者又在等待别的任务,这样一直下去,直到这个链条上的任务又在等待第一个任务释放锁。这就形成了一个相互等待的循环,没有那个线程能够继续。这被称之为死锁。
我们真正需要解决的问题是程序看起来可能工作良好,但是具有潜在的死锁风险。这时,死锁可能发生,而事先却没有任何征兆,所以缺陷会潜伏在你的程序里,直到被人以外的发现了。因此,在编写并发编程的时候,进行仔细的程序设计以防止死锁是非常关键的。
下面引入一个问题,一共有 5 个哲学家。这些哲学家将花部分时间思考,花部分时间就餐。作为哲学家他们很穷,所以他们只能买 5 根筷子。他们围坐在桌子的周围,每人之间放一根筷子。当一个哲学家要就餐的时候,这个哲学家必须同时得到左边和右边的筷子。如果一个哲学家左边或者右边已经得到筷子,那么这个哲学家就必须等待,直至可得到必须的筷子。
public class Chopstick {
private boolean taken = false;
public synchronized void take() throws InterruptedException{
while (taken) {
wait();
}
taken = true;
}
public synchronized void drop() {
taken = false;
notifyAll();
}
}
任何两个哲学家都不能使用同一根筷子。也就是不能同时 taken() 同一个筷子。另外如果一个 Chopstick 被一个哲学家获得,那么另一个哲学家可以 wait(),直到当前的这根筷子的持有者调用 drop() 结束使用。
public class Philosopher implements Runnable{
private Chopstick left;
private Chopstick right;
private final int id;
private final int ponderFactor;
private Random random = new Random(47);
public void pause() throws InterruptedException{
if (ponderFactor ==0) {
return;
}
TimeUnit.MICROSECONDS.sleep(random.nextInt(ponderFactor * 250));
}
protected Philosopher(Chopstick left, Chopstick right, int id, int ponderFactor) {
super();
this.left = left;
this.right = right;
this.id = id;
this.ponderFactor = ponderFactor;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()) {
System.out.println(this+开始思考);
pause();
System.out.println(this+开始拿左边的筷子);
left.take();
System.out.println(this+开始拿右边的筷子);
right.take();
System.out.println(this+开始就餐);
pause();
left.drop();
right.drop();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println(当前线程被中断了);
}
}
@Override
public String toString() {
// TODO Auto-generated method stub
return 哲学家的编号:+id;
}
}
在哲学家的任务中,每个哲学家都是不断的思考和吃饭。如果 ponderFactor 不为 0,则 pause() 就会休眠一会。通过这样的方法你会看到哲学家会思考一段时间。然后尝试着去获取左边和右边的筷子,随后再在吃饭上花掉一段随机的时间,之后重复此过程。
现在我们来建立这个程序的死锁版本:
public class DeadLockingDiningPhilosophers {
public static void main(String[] args) {
// TODO Auto-generated method stub
int ponder = 5;
int size = 5;
ExecutorService service = Executors.newCachedThreadPool();
Chopstick[] sChopsticks = new Chopstick[size];
for (int i = 0; i size; i++) {
sChopsticks[i] = new Chopstick();
}
for (int i = 0; i size; i++) {
//每一个哲学家都会持有他左边和右边的筷子对象
service.execute(new Philosopher(sChopsticks[i],sChopsticks[(i+1)%size],i,ponder));
}
System.out.println(执行结束);
service.shutdownNow();
}
}
执行的结果:
执行结束
哲学家的编号:2开始思考
哲学家的编号:4开始思考
哲学家的编号:1开始思考
哲学家的编号:0开始思考
哲学家的编号:3开始思考
当前线程被中断了
当前线程被中断了
当前线程被中断了
当前线程被中断了
当前线程被中断了
这个程序表示每一个哲学家都有可能要表示进餐,从而等待其临近的 Philosopher 放下他们的 Chopstick。这将会使得程序死锁。
要修正死锁必须明白,当以下四个条件同时满足时,就会发生死锁:
互斥条件。任务使用的资源中至少有一个是不能共享的。
至少有一个任务必须持有一个资源,并且正在等待获取一个当前被别的任务持有的资源。也就是说必须是拿着一根筷子等待另一个筷子。
资源不能被任务抢占,任务必须把资源释放当做普通事件。你不能抢别人手里的筷子。
必须有循环等待,这时一个任务等待其他任务所持有的资源,后者又在等待另一个任务所持有的资源,这样循环下去直到有一个任务等待第一个任务所持有的资源,使得大家都被锁住。
因为要发生死锁所有这些条件必须满足;所以要防止死锁的话只需要破坏其中一个就可以。在程序中防止死锁的最容易的办法就是破坏第四个循环条件。
public class FixedDiningPhilosophers {
public static void main(String[] args) throws Exception {
int ponder = 5;
if(args.length 0)
ponder = Integer.parseInt(args[0]);
int size = 5;
if(args.length 1)
size = Integer.parseInt(args[1]);
ExecutorService exec = Executors.newCachedThreadPool();
Chopstick[] sticks = new Chopstick[size];
for(int i = 0; i size; i++)
sticks[i] = new Chopstick();
for(int i = 0; i size; i++)
if(i (size-1))
exec.execute(new Philosopher(sticks[i], sticks[i+1], i, ponder));
else
exec.execute(new Philosopher(sticks[0], sticks[i], i, ponder));
if(args.length == 3 args[2].equals(timeout))
TimeUnit.SECONDS.sleep(5);
else {
System.out.println(Press 'Enter' to quit);
System.in.read();
}
exec.shutdownNow();
}
}
执行结果:
哲学家的编号:2开始拿左边的筷子
哲学家的编号:4开始思考
哲学家的编号:1开始思考
哲学家的编号:0开始拿左边的筷子
哲学家的编号:0开始拿右边的筷子
哲学家的编号:0开始就餐
哲学家的编号:3开始就餐
哲学家的编号:2开始拿右边的筷子
/.....
通过确保最后一个哲学家先拿起和放下左边的筷子,我们可以移除死锁,从而使得程序运行。Java 并没有对死锁提供类库上的支持;能否通过仔细的程序设计避免死锁需要我们自己努力。
java编程思想之并发(死锁)的更多相关文章
- Java编程思想 第21章 并发
这是在2013年的笔记整理.现在重新拿出来,放在网上,重新总结下. 两种基本的线程实现方式 以及中断 package thread; /** * * @author zjf * @create_tim ...
- 《Java编程思想》读书笔记(五)
前言:本文是<Java编程思想>读书笔记系列的最后一章,本章的内容很多,需要细读慢慢去理解,文中的示例最好在自己电脑上多运行几次,相关示例完整代码放在码云上了,码云地址:https://g ...
- Java编程思想 (1~10)
[注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第一章 对象导论 1.万物皆对象2.程序就是对象的集合3.每个对象都是由其它对象所构成 ...
- Java编程思想总结笔记The first chapter
总觉得书中太啰嗦,看完总结后方便日后回忆,本想偷懒网上找别人的总结,无奈找不到好的,只好自食其力,尽量总结得最好. 第一章 对象导论 看到对象导论觉得这本书 目录: 1.1 抽象过程1.2 每个对象 ...
- Java编程思想读书笔记(一)【对象导论】
2018年1月7日15:45:58 前言 作为学习Java语言的经典之作<Java编程思想>,常常被人提起.虽然这本书出版十年有余,但是内容还是很给力的.很多人说这本书不是很适合初学者,我 ...
- 《Java编程思想》读书笔记
前言 这个月一直没更新,就是一直在读这本<Java编程思想>,这本书可以在Java业界被传神的一本书,无论谁谈起这本书都说好,不管这个人是否真的读过这本书,都说啊,这本书很好.然后再看这边 ...
- Java编程思想(前十章)
Java编程思想 有C++编程基础的条件下, 前10章可以快速过一下,都是基本语法,不需要花太多时间. 着重中后段的一些章节,类型信息.泛型.容器.IO.并发等. 中文翻译版 阅读地址 对于一个架构师 ...
- 《Java编程思想第四版完整中文高清版.pdf》-笔记
D.2.1 安插自己的测试代码 插入下述“显式”计时代码,对程序进行评测: long start = System.currentTimeMillis(); // 要计时的运算代码放在这儿 long ...
- JAVA编程思想读书笔记(五)--多线程
接上篇JAVA编程思想读书笔记(四)--对象的克隆 No1: daemon Thread(守护线程) 参考http://blog.csdn.net/pony_maggie/article/detail ...
随机推荐
- chrome正受到自动测试软件的控制-----web自动化测试如何去掉这段提示
本文为原创文章 在web自动化测试的时候,特别是在用chrome浏览器的时候经常会出现 “chrome正受到自动测试软件的控制” 这样的一句提示, 这是因为安装chrome浏览器的时候没有设置允许调 ...
- Oracle 错误代码小结
ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件 ORA-00018: 超出最大会话数 ORA-00019: 超出最大会话许可数 ORA-00020: 超出 ...
- Linux系统——DHCP
DHCP定义DHCP服务是负责IP.掩码.网关地址.DNS地址等自动分发的软件服务DHCP的分配方式(1)自动分配:分配到一个IP地址后永久使用(2)手动分配:由DHCP服务器管理员专门指定IP地址( ...
- uva11107 后缀数组
题意给了n个串 然后计算 这些串中的子串在大于1/2的串中出现 求出这个串的最长长度. 将这些串用一个每出现的不同的字符拼起来 ,然后二分找lcp #include <iostream> ...
- AOAPC I: Beginning Algorithm Contests (Rujia Liu) Volume 6. Mathematical Concepts and Methods
uva 106 这题说的是 说计算 x^2 + y^2 = z^2 xyz 互质 然后计算个数和 在 N内 不在 勾股数之内的数的个数 然后去找需要的 维基百科上 看到 另 n*m*2 =b ...
- EditPlus 4.3.2502 中文版已经发布(12月5日更新)
新的版本修复了在之前某版本中键盘 End 键定位位置错误的问题.
- windows系统bat方式启动tomcat出现java.lang.OutOfmemoryError:PermGen Space 错误
1.问题情景: 在部署项目时,将两个应用部署到同一个tomcat下,通过startup.bat启动服务时,控制台出现出现java.lang.OutOfmemoryError:PermGen Space ...
- Spring,Struts2,MyBatis,Activiti,Maven,H2,Tomcat集成(一)——Maven,Tomcat,Spring集成
1. 创建Maven Web工程 (1) 磁盘上创建Maven工程所需要的文件夹结构如下: (2) 在与src同级目录中创建pom.xml文件: <project xm ...
- Linux基础命令---ln
ln 为指定的目录或者文件创建链接,如果没有指定链接名,那么会创建一个和源文件名字一样的链接. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fed ...
- zendstudio中加入对tpl文件的支持,用HTML Editor编辑器编辑
zendstudio中加入对tpl文件的支持,用HTML Editor编辑器编辑:ThinkPHP中默认使用的tpl在zendstudio中默认打开都是文本编辑器的,没有语法提示开发效率很低,直接设置 ...