java多线程(八)-死锁问题和java多线程总结
为了防止对共享受限资源的争夺,我们可以通过synchronized等方式来加锁,这个时候该线程就处于阻塞状态,设想这样一种情况,线程A等着线程B完成后才能执行,而线程B又等着线程C,而线程C又等着线程A。这三个任务之间相互循环等待,但是其实没有哪个任务能够执行,这种情况就发生了死锁。
有一个经典的哲学家就餐问题,可以更清晰的理解死锁问题。有N个哲学家围绕在一张圆形餐桌前,餐桌中间有一份面条,每个哲学家都只有一根筷子,放在他的左手边。(因为餐桌是圆形的,所以就是每两个哲学家之间有一根筷子),所以共计有N个哲学家,N个筷子, 哲学们家们有时候思考,思考时不需要获取其他共享资源;有时候吃面条,吃面条时需要两根筷子,哲学家需要先拿到他right手边的筷子,然后再去拿left手边的筷子。如果此时,left手边筷子不在桌子上(被边上的哲学家拿走了)。则哲学家就把right手边的筷子拿在手中等待。 (调用wait).等待left边的筷子被放下。如果哲学家吃完面条,则放下两根筷子 ,继续思考。
我们仅仅通过逻辑思考,就可以想到如果每个哲学家都拿到了他right手边的筷子,那么此时就发生了死锁,因为实际上桌子上,每个哲学家正好拿到了一根筷子,都在等待他left手边的筷子被放下,但是不会再有筷子被放下了.
代码demo:src\thread_runnable\DeadLockingDiningPhiosophers.java
class Chopstick{
private boolean taken = false;
//拿起筷子
public synchronized void take() throws InterruptedException{
while (taken){
wait();
}
TimeUnit.MILLISECONDS.sleep(100);
taken = true;
}
//放下筷子
public synchronized void drop(){
taken = false;
notify();
}
} //end of "class Chopstick" class Philosopher implements Runnable{
private Chopstick left;
private Chopstick right;
private final int id;
private int eatTime;
private int thinkTime;
private Random rand = new Random(42); //the Answer to Life, the Universe and Everything is 42 public Philosopher(Chopstick left, Chopstick right, int id, int eatTime, int thinkTime) {
super();
this.left = left;
this.right = right;
this.id = id;
this.eatTime = eatTime;
this.thinkTime = thinkTime;
} //思考/或者吃饭的一段时间。
private void pause(int time) throws InterruptedException{
TimeUnit.MILLISECONDS.sleep(rand.nextInt(time*20));
} @Override
public void run() {
// TODO Auto-generated method stub
try {
while (!Thread.interrupted()){
System.out.println(this + "thinking");
pause(thinkTime);
//哲学家开始吃饭了
System.out.println(this + "grabbing right");
right.take();
System.out.println(this + "grabbing left");
left.take();
System.out.println(this + "grabbing eating");
pause(eatTime);
//吃完了。可以放下筷子了
right.drop();
left.drop(); }
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println(this + " exiting via interrupt");
}
} @Override
public String toString() {
return "Philosopher id=" + id + "\t";
} }//end of "class Philosopher" public class DeadLockingDiningPhiosophers {
//哲学家和筷子的数量
private static final int N = 3;
private static final int eatTime = 20;
private static final int thinkTime = 3; public static void main(String[] args) throws Exception{
ExecutorService exec = Executors.newCachedThreadPool();
int ponder = 1;
Chopstick[] sticks = new Chopstick[N]; for (int i=0; i<N; i++){
sticks[i] = new Chopstick();
} for (int i=0; i<N; i++){
exec.execute(new Philosopher(sticks[i], sticks[(i+1)%N], i, eatTime, thinkTime));
} } }
代码分析: Chopstick对象有 take(拿起)和 drop(放下)两个动作,而哲学家对象呢,不管是吃饭过程,还是思考过程,都是模拟sleep随机的时间, 吃完饭之后,放下筷子,进行思考。不间断进行循环。
在demo中,3个线程,分别执行3个哲学家的任务, 同时也只有3个筷子。
按照我们的测试,有概率会发生死锁。为了增大死锁发生的概率,便于测试,我们将拿起筷子的时间延长了。(就是在take方法中sleep(100)).
进行测试,很快就发生了死锁。
其中一次的输出结果:
从控制台可以看出,程序一直在运行,但是哲学家们却不会再吃饭和思考了。
从输出信息看出,
1,0,2号哲学家依次拿起了right边的筷子 ,然后再准备拿起left边的筷子时,因为没有筷子了,而陷入了漫长的wait()中,这个时候,死锁发生了。程序死掉了。
对于哲学家就餐问题,我们可以想出一个避免死锁的方案,比如,对于其中的某一位哲学家,限定其先拿left边的筷子,再拿right边的筷子。(和其余的哲学家正好相反)。
死锁问题最难的地方是在于它是小概率性的,并且可能隐藏相当长的时间才会发生,并且每次发生死锁时,都是不可重现的。这在实际的项目中,会引起非常难以调试的bug。
而在实际项目中,必现的bug都容易解决,小概率的,不可重现的bug那才真的让人头疼。
程序避免死锁并不是件容易的事情,但是遵循以下原则则可以尽量避免死锁。
(1),使用锁的时间尽可能的短,考虑使用同步语句块来代替同步方法。
(2),尽量避免代码在同一个时刻需要多个锁。
(3),创建和使用一个大锁来代替若干把小锁,并且用这把锁用于互斥。
总结:
多线程问题算是java当中比较高级的内容了,当然因为能力有限,我的这几篇博客写的也非常肤浅。而实际编码中,是否应该使用多线程,也应该仔细斟酌。
使用多线程应该基于以下几个原因。
(1),处理交织在一起的很多任务。
(2),更高效的应用计算机资源。(比如多核cpu,等待I/O),
(3),更好的组织代码。
(4)更好的用户体验。(比如UI界面)
但是多线程也有一些缺点要注意。
(1),等待共享资源时,降低效率。
(2),上下文切换需要耗费额外的资源。
(3),多线程也会增加代码复杂度。
(4),可能会导致一些难以调试的bug。比如死锁。
(5),平台差异性。
如果线程问题过于复杂,java的多线程机制不能满足要求,那么应该使用类似Erlang这样的 专门面向并发的的函数性语言。
这几篇java多线程文章的demo代码下载地址 http://download.csdn.net/detail/yaowen369/9786452
---
作者: www.yaoxiaowen.com
github: https://github.com/yaowen369
java多线程(八)-死锁问题和java多线程总结的更多相关文章
- java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()(多线程编程之二)
本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: package com.zejian.test; /** * @author ...
- 【多线程】死锁与Java栈跟踪工具
今天面试有一道题,写一个死锁的程序,自己也是短路了,没写出来,回来写下. 死锁常见的情况是A线程持有a锁.阻塞于b锁,B线程持有b锁,阻塞于a锁,形成一个循环阻塞的状态. import java.ut ...
- java中多线程产生死锁的原因以及解决意见
1. java中导致死锁的原因 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结 ...
- Java多线程学习---------超详细总结(java 多线程 同步 数据传递 )
目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么 ...
- Java基础知识笔记(五:多线程的同步问题)
编写多线程程序往往是为了提高资源的利用率,或者提高程序的运行效率,或者更好地监控程序的运行过程等.多线程同步处理的目的是为了让多个线程协调地并发工作.对多线程进行同步处理可以通过同步方法和同步语句块实 ...
- Java面试题整理一(侧重多线程并发)
1..是否可以在static环境中访问非static变量? 答:static变量在Java中是属于类的,它在所有的实例中的值是一样的.当类被Java虚拟机载入的时候,会对static变量进行初始化.如 ...
- Java 多线程(六)之Java内存模型
目录 1. 并发编程的两个问题 2 CPU 缓存模型 2.1 CPU 和 主存 2.2 CPU Cache 2.3 CPU如何通过 Cache 与 主内存交互 2.4 CPU 缓存一致性问题 3 Ja ...
- Java 容器源码分析之HashMap多线程并发问题分析
并发问题的症状 多线程put后可能导致get死循环 从前我们的Java代码因为一些原因使用了HashMap这个东西,但是当时的程序是单线程的,一切都没有问题.后来,我们的程序性能有问题,所以需要变成多 ...
- 已看1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架、多线程(并发编程)、I/O(NIO)、Socket、JDBC、XML、反射等。[泛型]\
1.熟练的使用Java语言进行面向对象程序设计,有良好的编程习惯,熟悉常用的Java API,包括集合框架.多线程(并发编程).I/O(NIO).Socket.JDBC.XML.反射等.[泛型]\1* ...
- Java 学习(19):Java 多线程编程
Java 多线程编程 Java 给多线程编程提供了内置的支持.一个多线程程序包含两个或多个能并发运行的部分.程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径. 多线程是多任务的一种特 ...
随机推荐
- 前端测试框架Jest系列教程 -- Mock Functions
写在前面: 在写单元测试的时候有一个最重要的步骤就是Mock,我们通常会根据接口来Mock接口的实现,比如你要测试某个class中的某个方法,而这个方法又依赖了外部的一些接口的实现,从单元测试的角度来 ...
- pipelineDB初体验
官网:http://www.pipelinedb.com/ pipelineDB是基于postgres的stream数据库.完全兼容pg的东西. 由于产品需要解决性能这块瓶颈,老大让试试这款基于流计算 ...
- SpringCloud Feign对Hystrix(断路由)的支持
第一步:首先开启Feign对Hystrix的支持,在properties文件中添加以下配置: feign.hystrix.enabled=true. 第二步:在上一篇Feign的基础上添加Hystri ...
- JAVA9模块化详解(一)——模块化的定义
JAVA9模块化详解 前言 java9已经出来有一段时间了,今天向大家介绍一下java9的一个重要特性--模块化.模块化系统的主要目的如下: 更可靠的配置,通过制定明确的类的依赖关系代替以前那种易错的 ...
- Android开发之漫漫长途 Ⅷ——Android Binder(也许是最容易理解的)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- EventBus猜想 ----手把手带你自己实现一个EventBus
本文是什么 本文是一篇怀着推測角度学习一个未知东西(EventBus)的文章. 先推測EventBus是怎样实现的. 依据推測去模仿他的实现. 查看源代码.验证猜想. 更深入的去理解他. 转载请注明出 ...
- Python3.x和Python2.x的区别【转】
转载自:https://www.cnblogs.com/codingmylife/archive/2010/06/06/1752807.html 1.性能 Py3.0运行 pystone benchm ...
- 让你成功安装vscode中go的相关插件
注意:该演示环境是windows环境,linux和mac环境操作思路一样 vscode中有很多go的相关插件,非常好用如下:gocodegopkgsgo-outlinego-symbolsgurugo ...
- Platt SMO 和遗传算法优化 SVM
机器学习算法实践:Platt SMO 和遗传算法优化 SVM 之前实现了简单的SMO算法来优化SVM的对偶问题,其中在选取α的时候使用的是两重循环通过完全随机的方式选取,具体的实现参考<机器学习 ...
- 虚拟表dual。字符串函数UPPER,LOWER。&变量。INITCAP,LENGTH,SUBSTR
&自定义变量的用法: