本文正在参加「Java主题月 – Java Debug笔记活动」,详情查看<活动链接>
下面将面试中常见问题进行汇总,关于线程部分常遇见的问题如下:
1、使用过线程么?线程如何实现?
通过继承 Thread 类、实现Runnable 接口,在run方法中实现功能或业务逻辑。
2、线程中start和run方法有什么区别和联系?
调用start方法可启动线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,即:线程要执行的内容。
而run方法只是线程里面一个普通方法的调用而已,还是在主线程里执行。如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
public static void main(String args[]) {
Thread t = new Thread() {
public void run() {
pong();
}
};
t.start();
System.out.print("ping");
}
public static void pong() {
System.out.print("pong");
}
复制代码
输出结果: pingpong
public static void main(String args[]) {
Thread t = new Thread() {
public void run() {
pong();
}
};
t.run();
System.out.print("ping");
}
public static void pong() {
System.out.print("pong");
}
复制代码
输出结果:pongping
通过以上两个程序实例,可以很容易的区分出start()方法和run()方法的区别:
- t.start(); 该行代码相当于是启动线程
- t.run(); 该行代码相当于是使用t这个类中的run方法而已。
3、了解过线程死锁么?如何有效的避免线程死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外界作用下,它们都将无法进行下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。
4、项目中有没有用过线程池 ?怎么用的 ?
使用过。我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
通过使用线程池就可以解决这个问题,使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务。Java的线程池最核心是ThreadPoolExecutor类,线程池底层都是通过 ThreadPoolExecutor 来实现的:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
复制代码
其中参数的意思分别为:
corePoolSize
:线程池里最小线程数maximumPoolSize
:线程池里最大线程数量,超过最大线程时候会使用RejectedExecutionHandler
keepAliveTime
:线程最大的存活时间,超过这个时间就会被回收unit
:线程最大的存活时间的单位workQueue
:缓存需要执行的异步任务的队列threadFactory
:新建线程工厂handler
:拒绝策略,表示当workQueue
已满,且池中的线程数达到maximumPoolSize
时,线程池拒绝添加新任务时采取的策略。DiscardPolicy
:抛弃当前任务,DiscardOldestPolicy
:扔掉最旧的,CallerRunsPolicy
:由向线程池提交任务的线程来执行该任务,AbortPolicy
:抛出RejectedExecutionException
异常。