线程面试44题及答案(线程的基本方法)
线程面试44题及答案(线程的基本方法)interrupt方法用于向线程发送一个终止通知信号,会影响该线程内部的一个中断标识位,线程本身并不会因为调用了interrupt方法而改变状态(阻塞、终止等)。状态的具体变化需要等待接收到中断标识的程序的最终处理结果来判定。对interrupt方法的理解需要注意如下4个核心点。调用yield方法会使当前线程让出(释放)CPU时间片,与其他线程一起重新竞争CPU时间片。在一般情况下,优先级高的线程更有可能竞争到CPU时间片,但这不是绝对的,有的操作系统对线程的优先级并不敏感。线程相关的基本方法有wait、notify、notifyAll、setDaemon、sleep、join、yield、interrupt等,这些方法控制线程的运行,并影响线程的状态变化。调用wait方法的线程会进入Waiting状态,只有等到其他线程的通知或被中断后才会返回。需要注意的是,在调用wait方法后会释放对象
相关面试题(1)线程的run方法和start方法有什么区别?★★★★★
(2)Java中的notify方法和notifyAll方法有什么区别?★★★☆☆
(3)守护线程是什么?★★★☆☆
(4)sleep方法和wait方法有什么区别?★★★☆☆
线程的基本方法线程相关的基本方法有wait、notify、notifyAll、setDaemon、sleep、join、yield、interrupt等,这些方法控制线程的运行,并影响线程的状态变化。
1.线程等待:wait方法调用wait方法的线程会进入Waiting状态,只有等到其他线程的通知或被中断后才会返回。需要注意的是,在调用wait方法后会释放对象的锁,因此wait方法一般被用于同步方法或同步代码块中。
2. 线程睡眠:sleep方法sleep方法不会释放当前占用的锁,会导致线程进入超时等待状态;而wait方法会导致当前线程进入等待状态。
3. 线程让步:yield方法调用yield方法会使当前线程让出(释放)CPU时间片,与其他线程一起重新竞争CPU时间片。在一般情况下,优先级高的线程更有可能竞争到CPU时间片,但这不是绝对的,有的操作系统对线程的优先级并不敏感。
4.线程中断:interrupt方法interrupt方法用于向线程发送一个终止通知信号,会影响该线程内部的一个中断标识位,线程本身并不会因为调用了interrupt方法而改变状态(阻塞、终止等)。状态的具体变化需要等待接收到中断标识的程序的最终处理结果来判定。对interrupt方法的理解需要注意如下4个核心点。
◎ 调用interrupt方法并不会中断一个正在运行的线程,也就是说处于运行状态的线程并不会因为调用了interrupt方法而终止,仅仅改变了内部维护的中断标识位而已。具体的JDK源码如下:
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
◎ 如果因为调用sleep方法使线程处于超时等待状态,则这时调用interrupt方法会抛出InterruptedException,使线程提前结束超时等待状态。
- 许多声明抛出InterruptedException的方法如Thread.sleep(long mills),在抛出异常前都会清除中断标识位,所以在抛出异常后调用isInterrupted方法将会返回false。
- 中断状态是线程固有的一个标识位,可以通过此标识位安全终止线程。比如,在想终止一个线程时,可以先调用该线程的interrupt方法,然后在线程的run方法中根据该线程的isInterrupted方法的返回状态值安全终止线程。
public class SafeInterruptThread extends Thread {
@Override
public void run() {
if (!Thread.currentThread().isInterrupted()) {
try {
//1:这里处理正常的线程业务逻辑
sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); //重新设置中断标识
}
}
if (Thread.currentThread().isInterrupted()){
//2:处理线程结束前必要的一些资源释放和清理工作,比如释放锁、
//存储数据到持久化层、发出异常通知等,用于实现线程的安全退出
sleep(10);
}
}
}
//3:定义一个可安全退出的线程
SafeInterruptThread thread = new SafeInterruptThread();
//4:安全退出线程
thread.interrupt();
5. 线程加入:join方法
join方法用于等待其他线程终止,如果在当前线程中调用一个线程的join方法,则当前线程会转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取 CPU 的使用权。在很多情况下,主线程生成并启动了子线程,需要等到子线程返回结果并收集和处理再退出,这时就要用到join方法,具体的使用方法如下:
System.out.println("子线程运行开始!");
ChildThread childThread = new ChildThread();
childThread.join();//主线程(当前线程)阻塞等待子线程childThread执行结束
System.out.println("子线程join()结束,开始运行主线程");
6.线程唤醒:notify方法
Object类有个notify方法,用于唤醒在此对象监视器上等待的一个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,唤醒时选择是任意的。
我们通常调用其中一个对象的wait方法在对象的监视器上等待,直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他线程竞争。类似的方法还有notifyAll,用于唤醒在监视器上等待的所有线程。
7.后台守护线程:setDaemon方法setDaemon方法用于定义一个守护线程,也叫作“服务线程”,该线程是后台守护线程。后台守护线程有一个特性,即为用户线程提供公共服务,在没有用户线程可服务时会自动离开。
守护线程的优先级较低,用于为系统中的其他对象和线程提供服务。将一个用户线程设置为守护线程的方法是在线程对象创建之前用线程对象的setDaemon(true)来设置。
在后台守护线程中定义的线程也是后台守护线程。后台守护线程是JVM级别的,比如垃圾回收线程就是一个经典的守护线程,在我们的程序中不再有任何线程运行时,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以在回收JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态下运行,用于实时监控和管理系统中的可回收资源。
守护线程是运行在后台的一种特殊线程,其独立于控制终端并且周期性地执行某种任务或等待处理某些已发生的事件。也就是说,守护线程不依赖于终端,但是依赖于JVM,与JVM“同生共死”。在JVM中的所有线程都是守护线程时,JVM就可以退出了,如果还有一个或一个以上的非守护线程,则JVM不会退出。
8 .sleep方法和wait方法的区别sleep方法和wait方法的区别如下。
- ◎ sleep方法属于Thread类,wait方法则属于Object类。
- ◎ sleep方法暂停执行指定的时间,让出CPU给其他线程,但其监控状态依然保持,在指定的时间过后又会自动恢复运行状态。
- ◎ 在调用sleep方法时,线程不会释放对象锁。
- ◎ 在调用wait方法时,线程会放弃对象锁,进入等待锁池,只有针对此对象调用notify方法后,该线程才能进入对象锁池准备获取对象锁,并进入运行状态。
start方法和run方法的区别如下。
- ◎ start方法用于启动线程,真正实现了多线程运行。在调用了线程的start方法后,线程会在后台执行,无须等待 run 方法体的代码执行完毕,就可以继续执行下面的代码。
- ◎ 在通过调用Thread类的start方法启动一个线程时,此线程处于就绪状态,并没有运行。
- ◎ run方法也叫作线程体,包含了要执行的线程的逻辑代码,在调用run方法后,线程会进入运行状态,开始运行run方法中的代码。在run方法运行结束后,该线程终止,CPU再次调度其他线程。
内容摘自《Offer来了(第2版)》。这是一本超强Java面试宝典、面霸手册,超详尽的Java知识点速查,Java面试题库,帮你深入理解Java核心技术,对Java知识点查漏补缺,可作为工具书使用。