Java 多线程回顾
2021-03-07 本文已影响0人
fdsun
1 何为线程?
一个比进程更小的执行单位。
与进程不同的是:
- 同类的多个线程共享进程的 堆 和 方法区 资源,但每个线程有自己的 程序计数器 、虚拟机栈 和 本地方法栈
2 线程与进程的关系,区别及优缺点?
- 线程是进程划分成的更小的运行单位
- 同一进程中的 线程 极有可能会相互影响,而进程基本相互独立
- 线程执行开销小,但不利于资源的管理和保护
3 为什么程序计数器、虚拟机栈和本地⽅法栈是线程私有的呢?为什么堆和⽅法区是线程共享的呢?
- 程序计数器私有主要是为了线程切换后能恢复到正确的执⾏位置。
- 为了保证线程中的局部变量不被别的线程访问到,虚拟机栈(为JVM使用java方法服务)和本地⽅法栈(为JVM使用Native方法服务)是线程私有的
- 堆 ⽤于存放新创建的对象
- ⽅法区 主要⽤于存放已被加载的类信息、常量、静态变量
4 为什么要使用多线程
目的:提高程序的执行效率提高程序运行速度
- 从计算机底层来说:线程间的切换和调度的成本远远小于进程,多核 CPU 时代意味着多个线程可以同时运行
- 从当代互联网发展趋势来说:多线程并发编程正是开发高并发系统的基础
- 再深入到计算机底层来探讨:
- 单核时代: 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。
- 多核时代: 多核时代多线程主要是为了提高 CPU 利用率。
5 使用多线程可能带来的问题?
- 内存泄漏
- 死锁
- 线程不安全
6 线程的生命周期和状态?
Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态
- New : 初始状态, 线程被构建,但是还没有调用start()方法
- Runnable : 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称作“运行中”
- Blocked : 阻塞状态,表示线程阻塞于锁
- Waiting :等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
- Time_Waiting : 超时等待状态,该状态不同于Waiting ,它是可以在指定的时间自行返回
- Terminated : 终止状态,表示当前线程已经执行完毕
7 什么是上下文切换?
- 当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用CPU,这个过程就属于一次上下文切换。
上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。
8 什么是线程死锁? 如何避免死锁?
线程死锁:
- 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止
产生死锁必须具备以下四个条件:
- 互斥条件:该资源任意一个时刻只由一个线程占用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件 : 线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
- 循环等待条件 : 若干进程之间形成一种头尾相接的循环等待资源关系。
避免线程死锁:
- 破坏互斥条件 :这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
- 破坏请求与保持条件 :一次性申请所有的资源。
- 破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
- 破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
9 sleep() 方法和 wait() 方法区别和共同点?
- 两者最主要的区别在于:
sleep()
方法没有释放锁,而wait()
方法释放了锁 。 - wait() 通常被用于线程间交互/通信,sleep() 通常被用于暂停执行。
- 两者都可以暂停线程的执行。
10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
- 调用 start() 方法方可启动线程并使线程进入就绪状态,当分配到时间片后就可以开始运行了。直接执行 run() 方法的话不会以多线程的方式执行,而是会把它当成mian线程下的一个普通方法去执行。