- 什么是线程和进程?
进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运
行一个程序即是一个进程从创建,运行到消亡的过程。
线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可
以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个
线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在
各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量
级进程。
2.程序计数器为什么是私有的?
程序计数器主要有下面两个作用:
① 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,
如:顺序执行、选择、循环、异常处理。
②在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换
回来的时候能够知道该线程上次运行到哪儿了。
3.虚拟机栈和本地方法栈为什么是私有的?
虚拟机栈: 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操
作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧
在 Java 虚拟机栈中入栈和出栈的过程。
本地方法栈: 和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执 行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程
私有的。
4、说说并发与并行的区别?
并发: 同一时间段,多个任务都在执行 (单位时间内不一定同时执行);
并行: 单位时间内,多个任务同时执行。
5、为什么要使用多线程呢?
线程可以比作是轻量级的进程,是程序执行的最小单位,线程间
的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时
运行,这减少了线程上下文切换的开销。利用好多线程机制可以大大提高系统整体的并发能力以及性能。在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。多核时代多线程主要是为了提高进程利用多核 CPU 的能力。
6、使用多线程可能带来什么问题?
并发编程的目的就是为了能提高程序的执行效率提高程序运行速度,但是并发编程并不
总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、死
锁、线程不安全等等。
7、说说线程的生命周期和状态?
线程创建之后它将处于 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(终止) 状态。
8、什么是上下文切换?
线程在执行过程中会有自己的运行条件和状态(也称上下文),比如上文所说到过的程
序计数器,栈信息等。当出现如下情况的时候,线程会从占用 CPU 状态中退出。
主动让出 CPU,比如调用了 sleep() , wait() 等。
时间片用完,因为操作系统要防止一个线程或者进程长时间占用CPU导致其他线程
或者进程饿死。
调用了阻塞类型的系统中断,比如请求 IO,线程被阻塞。
被终止或结束运行
这其中前三种都会发生线程切换,线程切换意味着需要保存当前线程的上下文,留待线
程下次占用 CPU 的时候恢复现场。并加载下一个将要占用 CPU 的线程上下文。这就是
所谓的 上下文切换。
上下文切换是现代操作系统的基本功能,因其每次需要保存信息恢复信息,这将会占用
CPU,内存等系统资源进行处理,也就意味着效率会有一定损耗,如果频繁切换就会造
成整体效率低下。
9、 什么是线程死锁?如何避免死锁?
线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
避免死锁就是要破坏死锁产生的必要条件即可。
①破坏请求与保持条件 :一次性申请所有的资源。
② 破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可
以主动释放它占有的资源。
③ 破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反
序释放。破坏循环等待条件。
10、说说 sleep() 方法和 wait() 方法区别和共同点?
两者最主要的区别在于: sleep() 方法没有释放锁,而 wait() 方法释放了锁 。
两者都可以暂停线程的执行。
wait() 通常被用于线程间交互/通信, sleep() 通常被用于暂停执行。
wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的
notify() 或者 notifyAll() 方法。 sleep() 方法执行完成后,线程会自动苏醒。
或者可以使用 wait(long timeout) 超时后线程会自动苏醒。
11、为什么我们调用 start() 方法时会执行 run() 方法,为 什么我们不能直接调用 run() 方法?
new 一个 Thread,线程进入了新建状态。调用 start() 方法,会启动一个线程并使线程
进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准
备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 但是,直接执行
run() 方法,会把 run() 方法当成一个 main 线程下的普通方法去执行,并不会在某个
线程中执行它,所以这并不是多线程工作。