这两天忙于工作交接和面试,其中面试最常问的问题就是多线程,那么今天就聊聊多线程。
1、在java开发中,有几种方法可以实现一个线程?
2、Callable接口和Runnable接口的有什么区别?
3、如何停止一个正在运行的java线程?
4、volatile关键字是如何保证内存可见性?
5、线程的状态有哪些?
6、死锁产生的条件,如何避免死锁?
问题1:
主要有使用Runnable,Callable,Thread或者线程池。问题延伸,你工作中常用的线程池有哪些?如下
FixedThreadPool | 固定大小线程池 |
CachedThreadPool | 可变大小缓存池 |
ScheduledThreadPool | 可调度的线程池 |
问题2:
1)实现方法名称不一样,Callable规定的方法是call(),Runnable规定的方法是run()。
2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得。
3)call()可以抛出异常,run()不可以。
4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
问题3:
1)使用violate boolean变量来标识线程是否停止。
2)停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性。
3)对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO。问题4:
首先该问题是只有在多核CPU的情况下才会有。
volatile关键字有两层语义:
1、立即将缓存中数据写会到内存中
2、其他处理器通过嗅探总线上传播过来了数据监测自己缓存的值是不是过期了,如果过期了,就会对应的缓存中的数据置为无效。而当处理器对这个数据进行修改时,会重新从内存中把数据读取到缓存中进行处理。
在这种情况下,不同的CPU之间就可以感知其他CPU对变量的修改,并重新从内存中加载更新后的值,因此可以解决可见性问题。问题5:
分别是新建、就绪、运行、阻塞、死亡 一共5种状态。问题延伸-它们的状态是如何切换的?下面我用一张图说明:
问题6:
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。