一道真实的大厂笔试题,看看你能答对吗?
一、题目
下边这段代码输出什么呢?
public class MyThread extends Thread{
public void run(){
synchronized(this) {
System.out.println("A");
try{
wait();
}catch (InterruptedException e){}
System.out.println("B");
}
}
public static void main(String[] args)throws InterruptedException{
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
synchronized(thread){
thread.notify();
}
}
}
可以先自行分析下,再向下看解析哦~
二、解析
程序执行的流程如下:
1、创建并启动线程:
main 方法中创建了一个 MyThread 实例,并调用 thread.start() 启动线程。这会调用 MyThread 的 run 方法。
2、线程运行:
在 run 方法中,线程进入一个同步块,锁定当前对象 (this),并打印 “A”。
之后线程调用 wait() 方法,使当前线程进入等待状态,并释放锁。
3、主线程休眠:
main 方法中的主线程通过 Thread.sleep(1000) 休眠 1 秒,以确保 MyThread 有足够的时间打印 “A” 并进入等待状态。
4、主线程通知:
主线程休眠结束后,进入一个同步块,锁定 thread 对象,并调用 thread.notify() 唤醒在该对象上等待的线程。
唤醒并继续执行:
MyThread 线程被唤醒后,重新获得锁并继续执行同步块后面的代码,打印 “B”。
因此,最终的输出顺序为:
A
B
其中,“A” 是在线程进入等待状态前打印的,而 “B” 是线程被唤醒后打印的。
三、考查知识点
这段代码主要考查以下几个Java并发编程的知识点:
1、线程的创建和启动:
public class MyThread extends Thread {
public void run() {
// 线程执行的代码
}
}
- 通过继承 Thread 类并重写 run() 方法来创建一个新的线程类。
- 调用 start() 方法启动线程,线程会执行 run() 方法中的代码。
2、线程的同步:
synchronized(this) {
// 同步块
}
- synchronized 关键字用于创建同步块,确保同一时刻只有一个线程可以执行同步块内的代码。
- 这里同步块锁定的是当前对象 this。
3、线程的通信:
使用 wait() 和 notify() 方法在不同线程之间进行通信:
- wait():使当前线程等待,直到其他线程调用 notify() 或 notifyAll() 方法唤醒它。调用 wait() 方法时,线程会释放当前持有的锁,并进入等待状态。
- notify():唤醒在当前对象上等待的某个线程。如果有多个线程在当前对象上等待,notify() 只能唤醒其中一个,具体哪个线程被唤醒是不确定的。
- notifyAll():唤醒在当前对象上等待的所有线程。
4、线程状态:
理解线程的不同状态,包括新建、运行、阻塞、等待和终止:
- 新建(New):创建线程对象但尚未调用 start()。
- 运行(Runnable):调用 start() 方法后,线程进入就绪状态,可以被 CPU 调度执行。
- 阻塞(Blocked):线程在等待获取锁时进入的状态。
- 等待(Waiting):线程调用 wait() 方法后进入的状态,等待其他线程唤醒。
超时等待(Timed Waiting):线程调用带超时参数的方法(如 sleep(long millis) 或 wait(long timeout))后进入的状态。 - 终止(Terminated):线程执行完 run() 方法或者因异常退出后进入的状态。
5、异常处理:
try {
wait();
} catch (InterruptedException e) {
// 异常处理
}
- InterruptedException 是一个受检查异常,当线程在等待、睡眠或尝试获取锁时被中断,会抛出此异常。
- 需要在代码中进行捕获和处理。
这道题考查了多线程编程中的关键概念和技术,包括线程的创建、同步、通信、状态管理和异常处理。这些知识点在实际应用中非常重要,有助于编写高效、安全的并发程序。