多线程面试题总结
1、多线程有什么用
1.发挥多核CPU的优势
2.防止阻塞
单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。
但是单核CPU我们还是要应用多线程,就是为了防止阻塞
3.便于建模
假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。
但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,
并通过多线程分别运行这几个任务,那就简单很多了。
2、创建线程的方式第
方法:
继承于Thread类
实现Runnable接口
实现Callable接口
使用线程池:
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
3、Runnable接口和Callable接口的区别
Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为
多线程充满着未知性,某条线程是否执行了?
某条线程执行了多久?
某条线程执行的时候我们期望的数据是否已经赋值完毕?
无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。
而Callable+Future/FutureTask却可以获取多线程运行的结果,
可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务。真的是非常有用。
4、什么是线程安全
如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。
线程安全也是有几个级别的:
1)不可变
像String、Integer、Long这些,都是final类型的类,
任何一个线程都改变不了它们的值,要改变除非新创建一个,
因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用
2)绝对线程安全
不管运行时环境如何,调用者都不需要额外的同步措施。
要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,
不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet
3)相对线程安全
相对线程安全也就是我们通常意义上所说的线程安全,
像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,
如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,
99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。
4)线程非安全
这个就没什么好说的了,ArrayList、LinkedList、HashMap等都是线程非安全的类。
5、为什么要使用线程池
避免频繁地创建和销毁线程,达到线程对象的重用。
另外,使用线程池还可以根据项目灵活地控制并发的数目。
6.怎么检测一个线程是否持有对象监视器
Thread类提供了一个holdsLock(Object obj)方法,
当且仅当对象obj的监视器被某条线程持有的时候才会返回true,
注意这是一个static方法,这意味着"某条线程"指的是当前线程。