电话:18102818598
广东职场网欢迎您! 手机端
关闭
您当前的位置:首页 > 职场资讯 > 面试秘籍

程序员经典面试题:并发,容易带来哪些问题?

来源:广东职场网 时间:2021-10-11 作者:广州红树林教育科技有限公司 浏览量:

编程对于很多人来说,还是比较好上手的。当你学会了一门语言,可以编写一些程序了,很快就会遇到一道坎,并发编程,单线程下好好跑着的程序,怎么就运行异常了?怎么就得不到期望的结果。在面试中,并发编程也是经常出现,我们今天来讨论一个问题,并发编程,容易出哪些问题?

违反原子操作

相信大家在学习并发编程的时候,都会遇到这样一个经典问题,有一个函数,执行i=i 1,执行1000遍。在单线程的环境下,得到的结果都是预期的1000。如果是两个线程同时运行,那么,得到的结果可能是2000,也有可能小于2000。

这是因为i=i 1不是一个原子操作,我们会获取i的值,然后执行一次加法运算,最后将结果赋值给i。当多个线程执行的时候,获取到的i的值之后,在执行后面的动作之前,另外一个线程已经修改了i,造成最终的结果小于2000。

违反执行顺序

并发编程的时候,我们往往无法确定多个线程之间的执行顺序,经常我们会出现这样一个错误。我们在一个线程中使用的变量,会在另外一个线程中进行初始化或者赋值。最常见的,便是我们在主线程上创建一个子线程,然后再进行变量的初始化,子线程的执行可能早于父线程,造成程序错误。

锁与死锁

为了解决并发问题,我们通常会引入锁、信号、信号量等手段来保证临界区只会被一个线程访问,或者让一个线程等待另外一个线程执行完成。但是,引入锁之后,我们又可能会出现死锁的问题,例如:线程1与线程2都需要抢占AB两把锁,假设线程1先抢占了A锁,线程2抢占了B锁,这个时候,线程1在等待B锁,线程2在等待A锁,就这样,等到海枯石烂,我们称之为死锁。

出现死锁,需要同时满足下面几个条件:1.线程需要对需要的资源进行互斥访问(例如一个线程抢到锁)2.持有并等待(例如一个线程抢到了A锁,然后在等待B锁)3.非抢占(线程抢到了锁之后,不能被其他线程抢到)4.循环等待,线程之前存在一个环路。

那么,如何解决死锁的问题呢?既然出现死锁需要同时满足上面的几个条件,那么,我们只要破坏其中一个条件,就能够避免死锁问题。

不使用锁。死锁的需要对互斥资源进行访问,那么,我们能否不使用互斥资源呢?好在,当代的操作系统已经为我们提供了强大的原子操作指令,例如ComareAndSwap命令,在硬件上,保证Compare跟Swap在执行过程中不会被中断。

调整锁的顺序,避免产生循环等待。在上述例子中,线程1与线程2都需要AB两个锁,假如我们调整下顺序,都是先抢占A锁,再抢占B锁。这样就解决了循环等待的问题。

引入一个更高层级的锁,在现实开发中,我们很难去解决锁的顺序问题,我们的代码可能已经封装成类,库,并且锁的数量可能也非常多。那么,我们可以引入一个更高层级的锁,例如上述例子中,我们可以引入一个C锁,在程序执行之前,谁先抢到C锁,再去尝试抢占AB锁。当然,这样做的代价,会让程序的并发能力变低。

非抢占,我们一旦一个线程抢到锁之后,就会持有这个锁,其他线程都抢不到。那么,我们可以引入这样一种解决思路,如果一个线程,在等待下一个锁的时间过长,那么,我们就把之前的锁也释放掉,这样,别的线程就能够抢到这个锁,避免死锁的产生。

通过调度避免死锁,如果能够在程序设计之初,就避免死锁,那是再好不过了。上面的调整锁顺序是一种方式,避免抢占同一个锁的线程并发执行也是一种方式。特别是在嵌入式系统中,程序往往相对简单,我们可以通过这种方法进行解决。

死锁的检查与恢复。如果死锁是偶然发生的,例如几年才出现1次,但是解决死锁的难度又很大,我们可以等到死锁出现再解决。这就需要死锁检测技术,在检测到死锁之后,可以重启系统或者进行修复。

今天,我们了解到并发编程带来的问题与解决方案,希望对你在平时的工作或者面试有所帮助。

微信扫一扫分享资讯
客服服务热线
18102818598
9:00-21:00
微信公众号
手机浏览

Copyright C 20092014 All Rights Reserved 版权所有 红树林教育科技有限服务公司 粤ICP备19148605号

地址:广东省广州市花都区 EMAIL:gzhsljykj@163.com

人力资源证: 440114210011

Powered by PHPYun.

用微信扫一扫