线程面试题

2018-09-18  本文已影响6人  行者和他的钢笔

1、Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,他们有什么区别?

(1)sleep 是线程类(Thread)的方法,wait 是Object 类的方法。
(2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
(3)wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)
(4)sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常。

2、线程的sleep()方法和yield()方法有什么区别?

sleep()方法和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程。两者的区别在于:
(1)【运行机会,考虑不考虑其他线程的优先级】sleep()方法会给其他线程运行的机会,不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。
(2)【执行相应方法后的状态】当线程执行了sleep(long millis)方法,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法,将转到就绪状态。
(3)【是否抛异常】sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常。
(4)sleep()方法比yield()方法具有更好的可移植性。

3、请说出与线程同步以及线程调度相关的方法

-wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;

-sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;

-notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;

-notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

4、编写多线程程序有几种实现方式

(1)继承Thread类,重写 Thread 类的 run 方法。然后就是分配并启动该子类的实例
(2)实现Runnable 接口,该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
(3)Java 5以后创建线程还有第三种方式:通过线程池,实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值
Java 5

6、线程池

什么是线程池?【可以将线程池的作用一块回答】

线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用。
(1)减少了创建和销毁线程的次数,节省创建和销毁线程的时间和系统资源的开销。
(2)可以根据系统的承受能力,调整线程池中线程的数目,防止出现资源不足或内存消耗太多的情况。

创建线程池

通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。

Java四种线程池的使用:

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

使用线程池方式:
(1)使用线程池方式--Runnable接口
(2)使用线程池方式—Callable接口
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。

7、线程的基本状态以及状态之间的关系?

图片.png

【new:新建状态;wating:等待状态;runnable:就绪状态;running:运行状态;blocked:阻塞状态;dead:死亡状态】
1.新建
用new语句创建的线程对象处于新建状态,此时它和其他java对象一样,仅被分配了内存。

2.等待
当线程在new之后,并且在调用start方法前,线程处于等待状态。

3.就绪
当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态。处于这个状态的线程位于Java虚拟机的可运行池中,等待cpu的使用权。

4.运行状态

处于这个状态的线程占用CPU,执行程序代码。在并发运行环境中,如果计算机只有一个CPU,那么任何时刻只会有一个线程处于这个状态。
只有处于就绪状态的线程才有机会转到运行状态。

5.阻塞状态

阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才会有机会获得运行状态。

阻塞状态分为三种:

1、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

2、同步阻塞:运行的线程在获取对象同步锁时,若该同步锁被别的线程占用,则JVM会把线程放入锁池中。

3、其他阻塞:运行的线程执行Sleep()方法,或者发出I/O请求时,JVM会把线程设为阻塞状态。当Sleep()状态超时、或者I/O处理完毕时,线程重新转入就绪状态。

6.死亡状态

当线程执行完run()方法中的代码,或者遇到了未捕获的异常,就会退出run()方法,此时就进入死亡状态,该线程结束生命周期。

5、synchronized关键字的用法

线程同步的方式有两种:

方式1:同步代码块

同步代码块: 在代码块声明上 加上synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。


同步代码块执行原理

加了同步之后,线程进同步需要判断锁,获取锁,出同步需要释放锁,导致程序运行速度的下降

方式2:同步方法

好处:代码简洁

将共享数据和同步抽取到一个方法中

8、Lock接口

Lock提供了一个更加面对对象的锁,在该锁中提供了更多的操作锁的功能。我们使用Lock接口,以及其中的lock()方法和unlock()方法替代同步。

8、synchronized和java.util.concurrent.locks的区别

(1)synchronized和Lock接口都可以实现同步访问。
(2)synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
(3)在同步锁的释放上:
如果一个代码块或者一个方法被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
  1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
  2)线程执行发生异常,此时JVM会让线程自动释放锁。

如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

知识点总结

上一篇下一篇

猜你喜欢

热点阅读