当递归遇到synchronized
面试题:有一个synchronized方法,加入该方法发生递归调用,会导致线程死锁码? 解析:
所谓递归函数就是自调用函数,在函数体内直接或间接的调用自己,即函数的嵌套是函数本身。
递归方式有两种:直接递归和间接递归,直接递归就是在函数中出现调用函数本身。间接递归,指函数中调用了其他函数,而该其他函数又调用了本函数。
那什么时候使用递归呢?一般来说当你要在某段代码逻辑中使用循环迭代的时候但是迭代的次数在迭代之前无法知晓的情况下使用递归。打个比方你要在一个文件夹中查找某个文件,
而这个文件夹底下有N多子文件夹和文件,当你在不知道有多少层文件夹和文件的情况下你就得用到递归了。
递归的优点就是让代码显得很简洁,同时有些应用场景不得不使用递归比如前面说的找文件。递归是个好东西但是在某些时候也会给你带来一些麻烦。比如在多线程的环境下使用递归,
遇到了多线程那么就不得不面对同步的问题。而递归程序遇到同步的时候很容易出问题。多线程的递归就是指递归链中的某个方法由另外一个线程来操作,以下代码的意思都是这个意思即
调用recursive()和businessLogic()并非一个线程(如果是在一个线程中就不存在死锁问题,例如下面的recursive变成private就不存在问题。)
public class Test {
public void recursive(){
this.businessLogic();
}
public synchronized void businessLogic(){
System.out.println("处理业务逻辑");
System.out.println("保存到数据库");
this.recursive();
}
}
以上这段代码就是个能形成死锁的代码,事实上这个“synchronized”放在“businessLogic()”和“recursive()”都会形成死锁,并且是多线程的情况下就会锁住!
他的逻辑顺序是先执行recursive()方法然后接下来执行businessLogic()方法同时将businessLogic()方法锁住,接下来程序进入businessLogic()方法内部执行完
打印语句后开始执行recursive(),进入recursive()后准备执行businessLogic(),等等问题来了!之前执行的businessLogic()的锁还没有放开这次又执行到这里了,
当然是过不去的了,形成了死锁!从这个例子我们总结出来一个规律就是在递归的时候在递归链上面的方法上加锁肯定会出现死锁(所谓递归链就是指recursive()链向businessLogic(),
businessLogic()又链回recursive()),解决这个问题的方法就是避免在递归链上加锁,请看以下的例子
public class Test {
public void recursive(){
this.businessLogic();
}
public void businessLogic(){
System.out.println("处理业务逻辑");
this.saveToDB();
this.recursive();
}
public synchronized void saveToDB(){
System.out.println("保存到数据库");
}
}
saveToDB()不在这条递归链上面自然不会出现死锁,所以说在递归中加锁是件很危险的事情,实在逃不过要加锁就加在最小的粒度的程序代码上以减小死锁的概率。
当递归遇到synchronized的更多相关文章
- synchronized 是可重入锁吗?为什么?
什么是可重入锁? 关于什么是可重入锁,我们先来看一段维基百科的定义. 若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(re ...
- synchronized,ReentrantLock解决锁冲突,脏读的问题
最常见的秒杀系统,解决思路就是从前端.后台服务.数据库层层去掉负载,以达到平衡 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLo ...
- ConCurrent in Practice小记 (2)
Java-ConCurrent2.html :first-child{margin-top:0!important}img.plugin{box-shadow:0 1px 3px rgba(0,0,0 ...
- C、C++、Java、go的语法区别
详细C++.Java比较:http://www.cnblogs.com/stephen-liu74/archive/2011/07/27/2118660.html 一.C.C++的区别 在很大程度上, ...
- Java和C++的对比
事实上, Java 本来就是从 C++衍生出来的. C++和 Java 之间仍存在一些显著的差异.可以这样说,这些差异代表着技术的极大进步.一旦我们弄清楚了这些差异,就会理解为什么说 Java 是一种 ...
- 《Java编程思想第四版》附录 B 对比 C++和 Java
<Java编程思想第四版完整中文高清版.pdf>-笔记 附录 B 对比 C++和 Java “作为一名 C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且 Java 的语法无疑是 ...
- 【转】C++和Java比较
"作为一名C++程序员,我们早已掌握了面向对象程序设计的基本概念,而且Java的语法无疑是非常熟悉的.事实上,Java本来就是从C++衍生出来的." 然而,C++和Java之间仍存 ...
- Differences or similarities between Java and C++
“作为一名C++程序员,我们早已掌握了面向对象Object-oriented Programming程序设计的基本概念,而且Java的语法无疑是非常熟悉的.事实上,Java本来就是从C++衍生出来的. ...
- JAVA编程思想中总结的与C++的区别
Java和C++都是面向对象语言.也就是说,它们都能够实现面向对象思想(封装,继乘,多态).而由于c++为了照顾大量的C语言使用者,而兼容了C,使得自身仅仅成为了带类的C语言,多多少少影响了其面向对象 ...
随机推荐
- Go中的异常处理
1. errors包 Go 有一个预先定义的 error 接口类型 : type error interface { Error() string } 错误值用来表示异常状态.Go也提供了一个包:er ...
- Scala基础语法学习(一)
1. val和var的区别 val定义的是一个常量,无法改变其内容 scala> val s = 0 s: Int = 0 scala> s = 2 <console>:12: ...
- intellij idea与github整合管理代码
各位看官大家好,博主每每在公司学习新知识写代码时都需要通过U盘带回家来继续每天的学习,觉得这样实在麻烦,于是今天就整合了一下github来完成代码的管理. 开始之前我们需要准备三样东西:1.intel ...
- Fabric项目学习总结
1.Hyperledger Fabric的基本架构 2.PKI机制
- Mysql优化-mysql分区
背景:由于我负责i西科教务处系统,i西科用户量达到20000人左右,那么假设每人每星期10门讲课,数据库记录信息将是20万条,如果不将课程表进行分区或分表,就会造成爆表的情况,如此看来,分区是必须要做 ...
- JMeter的JavaRequest探究
1.背景 最近笔者的一位老朋友咨询了一个问题:在自定义的Java请求中如何编写多个请求?老朋友反应他们发送请求只能基于这种Java请求形式(代码调需用三方封装的jar包).这个问题恰巧不久前在笔者所在 ...
- Map集合的遍历(利用entry接口的方式)
核心思想: 调用map集合中的方法entrySet()将集合中的映射关系存放在Set集合中. 迭代Set集合 获取出的Set集合的元素是映射关系对象 通过映射关系对象方法的getKey(),getVa ...
- 三角函数与JavaScript
1. 三角函数 sin&(求对边与斜边的比值) cos&(邻边与斜边的比值) tan&(对边与邻边的比值) 2.JavaScript的函数的使用 Math.sin() Mat ...
- 前端小知识-js
一.对象冒充 function student(name,age){ this.name = name; this.age = age; this.show = function(){ console ...
- Linux配置部署_新手向(一)——CentOS系统安装
目录 前言 VMware 开始安装 系统安装 小结 @ 前言 最近忙过一件人生大事之后,终于稍微有点时间鼓捣东西,之前net core相关的基础已经鼓捣的差不多了,既然net core跨平台,那就来体 ...