并发编程最基础的12个面试连环炮,你答得上几个?

互联网全栈架构

共 4334字,需浏览 9分钟

 ·

2021-04-29 00:55

  

关注Java后端技术全栈

回复“000”获取大量电子书


在面试中,关于并发编程基础部分,如果你能接住以下12个连环炮,至少你的并发编程基础还是不错的。 

本文尽量站在面试官的角度来组织连环炮,目录如下:

1、创建线程有几种方式?

2、三种方式的优缺点是什么?

3、线程有哪些状态?

4、线程的生命周期是怎样的?

5、如何启动一个线程?

6、start方法和run方法有什么区别?

7、如何终止一个正在运行的线程呢?

8、什么叫做线程安全?

9、线程和进程有什么区别?

10、线程之间是怎么通信的?

11、线程中的yield()方法有什么作用?

12、yield()方法与sleep()方法的区别?

........继续更新中

1、创建线程有几种方式?

大家如果去网上搜,会发现很多种方式,七七八八的都有十来种了,但如果面试中遇到这个题目,回答下面三种就行了:

第一种,是继承Thread方式;

第二种,是实现Runnable接口方式;

第三种,是实现Callable接口方式。

还有就是网上很多文章都会提到线程池的方式,还有网上也有说法是创建线程池只有一种方式,但是我们这里回答上面三种即可。

这会面试官肯定会继续问三种的区别。

2、三种方式的优缺点是什么?

理解不了,就请你把下面的背下来:

  • 对于继承Thread方式,它的优点是简单易操作;但是,它的缺点却很致命,因为java中只支持单继承,如果使用这种方式,就无法再继承其他类了,大家可以想象一下,这种方式是多么的死板不灵活,所以不推荐使用。
  • 对于实现Runnable接口方式,它刚好解决了继承Thread方式的缺点,能够规避java中单继承的问题,可以实现多个接口。但它也有个缺点,就是无法拿到线程的返回值,其实这个缺点也还是可以容忍的,毕竟不是任何时候都需要返回值的。
  • 对于实现Callable接口方式,它完美解决了前面两种方式的不足,但是它也有缺点,就是编码复杂,这样就增大了我们程序员的使用难度。

回答了这些,面试官一般会认定你已经入门多线程了,但是他还会继续深挖,看你到底知道多少,通常,他会继续追问:线程有哪些状态?

3、线程有哪些状态?

关于这道题的答案,网上也存在分歧,这里我建议大家去java.lang.Thread类里看看,Thread类中有个内部类:State。这个内部类就是线程的状态。

从这个内部类中我们可以看到线程一共有六种状态:

第1种,(NEW)状初始态。

第2种,(RUNNABLE)运行状态。

第3种,(BLOCKED)阻塞状态。

第4种,(WAITING)等待状态。

第5种,(TIMED_WAITING)超时等待状态。

第6种,(TERMINATED)终止状态。

回答完上面几种状态后,面试官肯定会问:这些状态是怎么扭转的,或者线程的生命周期是怎样的?

4、线程的生命周期是怎样的?

这个问题是有点其实也很好回答,请看老田怎么给大家准备的。

1.new Thread表示NEW状态。

2.调用start方法来到运行(RUNNABLE)状态。

3.等待synchronized方法或模块的时候到(BLOCKED)阻塞状态。获取锁后又来到(RUNNABLE)状态。

4.调用Object的wait方法、Thread的join方法、LockSupport的park方法来到(WAITING)等待状态。反之,调用Object的notify方法、Object的notifyAll方法、LockSupport的unpark ( Thread )方法状态又回到(RUNNABLE)状态。

5.调用Thread的sleep(long)方法、Object的wait(long)方法、Thread的join(long)方法、LockSupport的parkNnos()方法、LockSupport的parkUnit方法来到(TIMED_WAITING)超时等待状态。反之,调用Object的notify方法、Object的notifyAll方法、LockSupport的unpark ( Thread )方法状态又回到(RUNNABLE)状态。

6.线程执行完成后来到(TERMINATED)终止状态。


注意:面试官可能会问你,(RUNNABLE)状态就一定执行吗?不一定哈,有两种情况:一种就是等待就绪状态,另外一种就是运行中。只是这里我们把运行中RUNNING和继续状态READY归纳为RUNNABLE状态了。

面试官这会觉得,小伙子还可以嘛,然后可能会接着问:如何启动一个线程?

5、如何启动一个线程?

这个问题就很简单了,笔试中遇到的概率那是相当的大,请留意哈。

启动方式很简单,就是调用线程的start方法,即可启动一个线程。

这个面试题中,很多人最容易搞混的就是start方法和run方法,面试官很有可能会问:start方法和run方法有什么区别?

6、start方法和run方法有什么区别?

这个面试题也是相当的简单哈。

启动线程是调用线程的start方法,不是run方法。

如果我们直接调用run方法就和我们调用一个普通实例对象的方法一样,不是创建一个线程,仅仅就是一次普通实例对象的方法调用,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,这样就没有达到启动线程的目的。

前面,我们说到启动一个线程的方法,面试官肯定会想到:如何终止一个正在运行的线程呢?

7、如何终止一个正在运行的线程呢?

既然有启动线程,我们不管是为了应对面试,还是学技术。都得有始有终嘛,所以肯定会联想到是否有关闭线程或者终止线程。这个问题也不难,你可以这么回答:

终止线程常见三种方式:

  • 调用stop方法,使用stop方法强行终止,但是这种方式一般不推荐使用。就像没有人会推荐你,使用强行关机的方式关闭正在运行的电脑一样,使用stop方法强行终止线程,也许会发生不可预料的结果,所以还是少用为好;
  • 使用interrupt方法中断线程;
  • 使用退出标志,使线程正常退出。也就是,当run方法完成后线程正常结束。

答出这三种方式,面试官会觉得你对多线程还是有一定的了解的,接下来,他或许会问你:什么叫做线程安全?

8、什么叫做线程安全?

”安全重于一切“,在程序界,也有这么一条不成文的规则。java中的各种技术,都会涉及到安全相关的内容,多线程也是如此。

首先,你得说说线程安全的定义,线程安全的定义是基于一种场景假设来的,假设你的代码在多线程下执行和在单线程下执行,永远都能获得一样的结果,那么你的代码就是线程安全的。

简单否?觉得不简单,希望你多读几遍,多体会体会。

扯了半天,面试官肯定会继续问的,或者笔试中很容易遇到的,也是一个老掉牙的问题:线程和进程有什么区别?

9、线程和进程有什么区别?

这道题呢,说容易也容易,说困难也困难。毕竟,每个程序员都会接触进程和线程,面试的时候,肯定也是能说出点什么来的。

但同时呢,这道题目又可以无限扩展和深入,想答好也是不容易的,大家可以从多个方面来切入,多回答几个方面的区别。

建议使用下面这种方式回答:

第一,从概念上来说,进程是一个“执行中的程序”,是系统进行资源分配和调度的一个独立单位;线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

第二,从CPU层面来说,进程和线程其实都是CPU工作时间段的描述,只不过是颗粒大小不同。

第三,从地址空间来说,同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间;

前面聊了半天都是基于单线程的,单线程的咱们都不怕,怕的就是多线程。面试官这时候很有可能会想到:线程之间是怎么通信的?

10、线程之间是怎么通信的?

在生活中,你的一个网友,如果有微信,那可以使用微信聊天,没有微信的情况下有邮箱,那就使用邮箱联系。那线程之间是怎么联系的?

可以回答下面两种:

第一种方式,就是内存共享, 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式的进行通信。

第二种方式,就是消息传递。顾名思义,就是通过明确的发送消息来显式的进行通信。这里又经常会涉及到等待唤醒机制:wait() 和 notify()  等相关问题了。

问完这些问题后,面试官可能会挑几个线程相关的,也是面试频率相对较高的题目。

比如:线程中的yield()方法有什么作用?

11、线程中的yield()方法有什么作用?

从字面意义上来说,我们可以猜到yield()方法应该是和让步有关的。

在多线程里,yield()方法,是线程让步的意思。当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行。这里需要注意了,使用yield()方法,是让自己或者其他线程运行,而并不是单纯的让给其他线程。也就是说,调用yield()方法后,该线程会让出CPU资源,但也有可能马上又是该线程获取了CPU执行权。

当你回答完后,面试官可能立马又会问:yield()方法与sleep()方法的区别?

12、yield()方法与sleep()方法的区别?

对于这道题,可以从四个点来回答:

第一,从线程优先级来说,yield()方法只会给相同优先级或者更高优先级的线程运行机会;但是,sleep()方法给其他线程运行机会时不考虑线程的优先级,因此也会给优先级低的线程运行的机会。

第二,从运行后状态来说,运行yield()方法之后,当前线程会重新回到可执行状态,所以执行yield()方法后,那个线程有可能又马上被执行;但是,如果执行sleep()方法后,线程会转入阻塞状态,所以,执行sleep()方法的线程在指定的时间内肯定是不会被执行的。

第三,从抛出异常来说,yield()方法没有声明任何异常;而sleep()方法声明抛出InterruptedException。

第四,从可移植性上来说,sleep()方法比yield()方法具有更好的可移植性。

总结

以上12连环炮,你能接住几个?如果全部接住,那恭喜你,你的并发编程基础掌握的还算可以。

关于相关连环炮正在更新中......


推荐阅读:

数据库系统设计概述

Kafka原理篇:图解kakfa架构原理

架构设计方法论

从面试角度一文学完 Kafka

数据库跟缓存的双写一致性

全网最详尽的负载均衡原理图解


互联网全栈架构

浏览 33
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报