最近开始准备秋招了,今天来总结一下面试中常常遇到有关Java并发的面试题,若有不当请多指教!java
什么是线程和进程?
请简要描述线程与进程的关系,区别及优缺点?
说说并发与并行的区别?
说说线程的生命周期和状态?
什么是上下文切换?
什么是线程死锁?如何避免死锁?
说说 sleep() 方法和 wait() 方法区别和共同点?
为何咱们调用 start() 方法时会执行 run() 方法,为何咱们不能直接调用 run() 方法?
- 进程就是在运行过程当中的程序,就好像手机运行中的微信,QQ,这些就叫作进程。
- 线程就是进程的执行单元,就好像一个音乐软件能够听音乐,下载音乐,这些任务都是由线程来完成的。
线程与进程类似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程当中能够产生多个线程。与进程不一样的是同类的多个线程共享进程的堆和方法区资源,但每一个线程有本身的程序计数器、虚拟机栈和本地方法栈,因此系统在产生一个线程,或是在各个线程之间做切换工做时,负担要比进程小得多,也正由于如此,线程也被称为轻量级进程。git
从JVM角度说进程和线程之间的关系
从上图能够看出:一个进程中能够有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 以后的元空间)资源,可是每一个线程有本身的程序计数器、虚拟机栈 和本地方法栈。github
扩展知识:为何程序计数器、虚拟机栈和本地方法栈是线程私有的呢?为何堆和方法区是线程共享的呢?面试
程序计数器为何是私有的?
程序计数器主要有下面两个做用:编程
- 字节码解释器经过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
- 在多线程的状况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候可以知道该线程上次运行到哪儿了。
因此,程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。微信
虚拟机栈和本地方法栈为何是私有的?
因此,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。多线程
堆和方法区
堆和方法区是全部线程共享的资源,其中堆是进程中最大的一块内存,主要用于存放新建立的对象 (全部对象都在这里分配内存),方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。并发
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不一样状态的其中一个状态ide
线程建立以后它将处于 NEW(新建) 状态,调用 start()
方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程得到了 CPU 时间片(timeslice)后就处于 RUNNING(运行) 状态。
当线程执行 wait()
方法以后,线程进入 WAITING(等待) 状态。进入等待状态的线程须要依靠其余线程的通知才可以返回到运行状态,而 TIME_WAITING(超时等待) 状态至关于在等待状态的基础上增长了超时限制,好比经过 sleep(long millis)
方法或 wait(long millis)
方法能够将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的状况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的run()
方法以后将会进入到 TERMINATED(终止) 状态。ui
多线程编程中通常线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能获得有效执行,CPU 采起的策略是为每一个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会从新处于就绪状态让给其余线程使用,这个过程就属于一次上下文切换。
什么是死锁
多个线程同时被阻塞,它们中的一个或者所有都在等待某个资源被释放。因为线程被无限期地阻塞,所以程序不可能正常终止。
举个例子:线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,因此这两个线程就会互相等待而进入死锁状态。
产生死锁的条件
产生死锁必须具有如下四个条件:
- 互斥条件:该资源任意一个时刻只由一个线程占用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
- 不剥夺条件:线程已得到的资源在末使用完以前不能被其余线程强行剥夺,只有本身使用完毕后才释放资源。
- 循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。
如何避免线程死锁?
咱们只要破坏产生死锁的四个条件中的其中一个就能够了。
破坏互斥条件
这个条件咱们没有办法破坏,由于咱们用锁原本就是想让他们互斥的(临界资源须要互斥访问)。
破坏请求与保持条件
一次性申请全部的资源。
破坏不剥夺条件
占用部分资源的线程进一步申请其余资源时,若是申请不到,能够主动释放它占有的资源。
破坏循环等待条件
靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
new一个 Thread,线程进入了新建状态;调用 start()
方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就能够开始运行了。 start()
会执行线程的相应准备工做,而后自动执行 run()
方法的内容,这是真正的多线程工做。 而直接执行 run()
方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,因此这并非多线程工做。