Java并发编程常见面试题
1.什么是线程和进程?
进程是操作系统分配资源的最小单位,各个进程之间占据独立的寻址空间,运行也是独立运行,进程间通信需要一些机制。进程间切换需要的开销较大。
线程是程序执行的基本单位,一个进程可以开启多个线程,他们的很多空间,比如堆空间,其他资源都是公用的。对于java来说,线程有自己的虚拟机栈,本地方法栈,和程序计数器。线程执行开销小,但是不够安全。
2. 虚拟机栈和本地方法栈,程序计数器为什么是私有的?
- 虚拟机栈:每个java方法在执行的时候,都会创建栈帧,来记录局部变量,操作数栈,引用等等。为了保证线程的局部变量不被别的线程访问,应该设为私有。
- 本地方法栈道理类似。程序计数器是记录方法执行到了哪里,用来在线程切换的时候恢复到正确的位置,自然要一个线程一个。
3.线程的生命周期与状态
线程创建之后处于New阶段,调用start()方法之后,处于ready阶段,获得cpu时间片时处于running阶段。
但是操作系统笼统认为处于runnable阶段。
执行wait()、join()之后,进入WAITING阶段,等待别的线程的通知,才能正常运行,此时释放资源。
执行sleep(long millis),wait(long millis)之后,进入TIME_WAITING阶段。时间过了就回到Runnable阶段。
在调用同步方法,却没有获得锁的时候,进入block阶段,执行完run方法,进入TERMINATED阶段。
4. 什么是线程死锁
持有对方想要的资源,期待获得对方持有的资源,形成了一个圈。互相等待谁也不让。
public class DeadLockDemo{private static Object obj1 = new Object();private static Object obj2 = new Object();public static void main(String[] args){new Thread(()->{synchronized(obj1){System.out.println(Thread.currentThread()+"get obj1");try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread()+"waiting obj2");synchronized(obj2){System.out.println(Thread.currentThread()+"get obj2");}}},"thread-1").start();new Thread(()->{synchronized(obj2){System.out.println(Thread.currentThread()+"get obj2");try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread()+"waiting obj1");synchronized(obj1){System.out.println(Thread.currentThread()+"get obj1");}}},"thread-2").start();}
}
5.如何避免线程死锁?
死锁产生的四个条件:
1.互斥。该资源任意时刻只能由一个线程占用。
2.请求与保持:一个进程因为因请求资源而阻塞时,自己占有的资源不会释放。
3.不剥夺条件:线程自己获得的只能自己使用完毕自己释放,不能被强行剥夺。
4.循环等待:互相持有和等待资源需要形成一个头尾相接的闭环。
避免死锁:
1.破坏请求与保持条件:占用部分资源的线程进一步申请其他资源的时候,如果申请不到,可以主动释放资源。
2.破坏不剥夺条件:一次性请求所有资源。
3.破坏循环等待条件:按照顺序请求资源,避免循环等待情况。
6.sleep()和wait()的区别和共同点?
1.sleep()方法没有释放锁,wait()会释放锁。
2.sleep()通常用来暂停线程。到了时间会自动苏醒。wait()通常用来线程间交互/通信,不会自动苏醒,需要其他线程调用notify()或者notifyAll()。
7.调用start方法会执行run()方法,为什么不能直接调用run()方法?
start()会执行线程的准备工作,然后得到时间片后就可以执行run()方法的内容了。但是run()方法本质上是一个普通方法。如果直接执行,那就没有新建线程,就是在原来的线程执行的。只有执行了start()方法,才能启动多线程。