Java并发面试题上
大约 4 分钟
Java并发面试题上
1. 什么是线程和进程?
- 进程 是程序的执行实例,具有独立的地址空间和资源。每个进程之间相互隔离,操作系统为每个进程分配独立的内存和资源。
- 线程 是进程中的最小执行单位,多个线程共享进程的内存和资源,因此它们比进程更轻量。线程在同一进程中执行并共享数据。
区别:
- 进程有独立的内存空间,而线程共享进程的内存。
- 线程创建和销毁的开销比进程小。
- 线程间的通信比进程间通信更高效。
2. Java 线程与操作系统线程的区别?
Java 线程是操作系统线程的一个抽象,在 Java 中创建的线程实际是操作系统的线程的封装。Java 提供了线程管理的 API,如 Thread 和 Runnable,而底层的线程调度和管理由操作系统内核处理。
3. 线程的生命周期和状态?
线程的生命周期包括:
- 新建(New):线程对象被创建,但未开始执行。
- 就绪(Runnable):线程准备好执行,等待 CPU 调度。
- 运行(Running):线程分配 CPU 时间片并开始执行。
- 阻塞(Blocked):线程因为等待资源(如文件操作、锁等)而阻塞。
- 等待(Waiting):线程主动等待某些条件(如
wait())。 - 超时等待(Timed Waiting):线程等待指定时间。
- 终止(Terminated):线程执行结束或因异常终止。
4. 线程上下文切换是什么?
上下文切换是操作系统在不同线程之间切换时保存当前线程的状态,并恢复下一个线程状态的过程。涉及保存寄存器、程序计数器等信息,切换后,CPU 将继续执行新线程的任务。
5. Thread#sleep() 和 Object#wait() 方法对比?
- Thread.sleep(long millis):使当前线程进入休眠状态,暂停指定时间,期间不释放锁。
- Object.wait():让当前线程等待,并释放持有的锁,直到被其他线程通过
notify()或notifyAll()唤醒。
wait() 不是定义在 Thread 类中,而是定义在 Object 类中,因为它是所有对象的公共方法(所有对象都可以作为锁使用)。
6. 可以直接调用 Thread 类的 run() 方法吗?
可以,但不建议。调用 run() 方法并不会启动新线程,而是直接在当前线程中执行 run() 方法。若想启动新线程,应该使用 start() 方法,它会触发线程的生命周期,并由操作系统调度。
7. 多线程并发与并行的区别?
- 并发(Concurrency):多个线程交替执行,操作系统在单核 CPU 上通过上下文切换实现并发执行。
- 并行(Parallelism):多个线程同时执行,在多核 CPU 上,多个线程可以真正并行运行。
8. 如何创建线程?
在 Java 中,线程可以通过两种方式创建:
- 继承
Thread类:创建一个子类,重写run()方法,并调用start()启动线程。 - 实现
Runnable接口:实现Runnable接口的run()方法,创建Thread对象并调用start()。
9. 为什么要使用多线程?
- 提高程序效率,尤其是 I/O 密集型或计算密集型任务可以并行处理。
- 提供更好的响应性,UI 线程可以处理用户输入,后台线程处理任务。
- 利用多核 CPU 实现并行处理。
10. 死锁是什么,如何避免?
死锁是两个或多个线程互相等待对方释放资源,导致程序无法继续执行。常见的死锁条件包括:
- 互斥:至少一个资源必须处于互斥状态。
- 占有并等待:一个线程已经持有一个资源并等待其他资源。
- 不可抢占:线程持有的资源不能被强制释放。
- 循环等待:多个线程形成环形等待。
避免死锁:
- 尽量避免嵌套锁,按统一的顺序请求锁。
- 使用超时机制(如
tryLock)避免无限等待。 - 使用
Lock接口(如ReentrantLock)而非synchronized,因其可以设置公平锁和尝试锁。
11. Java线程池的作用?
线程池是管理和复用线程的机制,它避免了频繁创建和销毁线程的开销。Java 提供了 Executor 框架来实现线程池,如 Executors.newFixedThreadPool()。
12. Java 线程安全的实现方式?
- 使用
synchronized关键字:对临界区代码块进行同步,保证同一时刻只有一个线程执行。 - 使用
Lock接口:显式地获取和释放锁,比synchronized提供了更细粒度的控制。 - 使用
Concurrent包中的类:如ConcurrentHashMap、CopyOnWriteArrayList等,提供高效的线程安全操作。
