一、为什么要使用多线程?
-
单核CPU时代,如果要运行多线程,效率反而会降低,因为多个线程是顺序执行的,同一时间只能有一个线程运行,只是系统在频繁地切换线程而已,给每个线程分配时间片来执行,看上去是同时跑的,但是频繁切换线程反而会增加开销。
-
现在是多核CPU时代,更加适用于多线程,如果还在使用单线程开发,只用到了单核CPU,没有发挥多核CPU的优势,是对CPU资源的浪费,当然也要基于实际的业务模式来合理选择。
二、创建线程的方式有哪些?
-
实现Runnable接口
-
优点:实现Runnable接口之后,还可以继承其他类
-
缺点:如果要访问当前线程,必须使用Thread.currentThread()
-
-
继承Thread类
-
优点:访问当前线程,使用this即可
-
缺点:不能再继承其他父类
-
-
通过Callable接口和Future接口创建(返回值Future,用来接收多线程执行结果)
- 优缺点和第二种方法类似
-
通过线程池创建(与以上三种方式不在一个维度)
-
优点:提高资源(内存、CPU)利用率;提高程序执行效率
-
缺点:资源(内存、CPU)开销大;程序复杂度上升
-
三、Runnable和Callable的区别?
前者是重写run方法;后者是call方法
不能抛异常;可以抛异常
通过Callable可以得到一个Future对象,它具备一切功能,比如了解任务执行情况,也可以取消任务
四、线程的五种状态及含义?
-
新建:线程对象被创建
-
就绪:其他线程(例如main线程)调用了该线程对象的start()方法,该线程等待被调度
-
运行:该线程获得CPU时间片,被执行
-
阻塞:该线程因为某种原因放弃CPU使用权,暂停运行
-
同步阻塞synchronized:运行中的线程在获取某个资源的同步锁时,该同步锁被其他线程占用,则JVM会将该线程放入锁池中
-
等待阻塞wait:运行中的线程调用wait()方法,JVM会将该线程放入等待队列
-
其他阻塞:运行中的线程执行sleep()或者join()方法,JVM会将该线程置为阻塞状态。当状态超时或者线程终止,该线程则会重新变为运行状态
-
-
死亡:main()方法执行结束,或者因为异常退出run()方法,则该线程结束生命周期,不再复生
五、什么是线程池?
-
线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考虑使用线程池来提升系统的性能。
-
做个类比:当你需要看一本书的时候,你会去书店买(创建线程),然后看完之后可能就扔了(销毁线程),这个过程太浪费资源(比如你买书的钱、地球森林资源等)。
-
但是当你选择图书馆、附近的流动自助借阅机(线程池)去借,看完之后还回去,达到重复利用,这个过程浪费的金钱成本、时间成本就少得多。所以这个例子就很好证明了线程池的好处。
六、为什么要使用线程池?有什么优点?
-
降低资源消耗:通过重复利用已经创建的线程,降低创建、销毁线程带来的资源消耗
-
提高响应速度:当任务到达时,不需要等待线程被创建就可以立即执行
-
控制并发数:因为线程池通常会设置最大运行线程数,其他任务需要等待线程被释放方可执行,所以并发数得以被控制
-
支持定时执行、单线程线程池等
七、如何创建线程池?
在JUC(java.util.concurrent)并发工具包中,有一个Executor接口可以用来创建线程池,通常有4种线程池,分别是:
- 可缓存线程池(newCachedThreadPool)
- 单线程线程池(newSingleThreadExecutor )
- 定长线程池(newFixedThreadPool)
- 定时线程池(newScheduledThreadPool)
一般常用的是可缓存线程池(newChaceThreadPool)
八、 线程B怎么知道线程A修改了变量?(也就是怎么保障可见性)
-
volatile修饰变量:保证共享变量的可见性,值被修改之后,会立马更新到主内存,下一个线程读到的就是最新值
-
synchronized修饰修改变量的方法:单个线程修改共享变量之后,其他线程才能操作,就可以保证可见性
-
wait/notify:其他线程等待被唤醒才会执行,也可以保证可见性
-
while轮询
九、多线程同步的实现方式有哪些?
-
Synchronized关键字
-
Lock-unlock
-
分布式锁
十、sleep和wait方法有什么区别?
-
唤醒方式不同:线程执行sleep()方法后,需要等到超时或者中断才能唤醒;线程执行wait()方法后,需要等待其他线程调用notify()方法进行唤醒
-
方法属性不同:sleep()方法是静态方法;wait()方法是实例方法
-
是否释放锁资源:sleep()方法不会释放;wait()方法会释放
-
作用对象不同:sleep()方法作用于当前线程;wait()方法作用于对象本身
-
出现的区域不同:sleep()方法不需要在同步方法、同步代码块中被调用;wait()方法需要