Java 多线程高级面试题
1. 什么是自旋锁?
自旋锁是指线程在获取锁时,如果锁不可用,线程不会立即阻塞,而是在循环中不断尝试获取锁,直到获取成功为止。
2. Java内存模型是什么?
Java内存模型(JMM)定义了程序中变量的访问规则,以及在多线程环境下,这些变量如何与内存交互,确保数据的可见性和一致性。
3. 什么是CAS?
CAS(Compare-And-Swap)是一种无锁的非阻塞算法,用于实现多线程环境下的原子操作。
4. 什么是AQS?
AQS(AbstractQueuedSynchronizer)是Java并发包中一个用于构建锁和其他同步器的框架。
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {}
5. 什么是乐观锁和悲观锁?
乐观锁假设多线程并发时不会发生冲突,只在提交更新时检查是否存在冲突;悲观锁假设会发生冲突,一开始就加锁。
6. `Executors`框架的作用是什么?
Executors
框架用于简化线程的创建和管理,提供了线程池的实现。
7. 什么是阻塞队列?如何使用阻塞队列来实现生产者-消费者模型?
阻塞队列是一种在尝试获取或添加元素时,如果操作无法立即完成,会阻塞当前线程的队列。
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
producer.start();
consumer.start();
8. `Callable`和`Future`的区别是什么?
Callable
可以返回值和抛出异常,而Future
用于跟踪异步计算的结果。
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(new Callable<Integer>() {
public Integer call() {
return 123;
}
});
9. `FutureTask`的作用是什么?
FutureTask
是一个实现了Runnable
和Future
接口的类,用于包装Callable
任务。
10. 什么是同步容器和并发容器的实现?
同步容器是指通过同步机制保证线程安全的容器,如Vector
和Hashtable
;并发容器是指通过并发控制机制保证线程安全的容器,如ConcurrentHashMap
。
11. `synchronized`和`ReentrantLock`有什么区别?
synchronized
是内置的同步机制,而ReentrantLock
是Java并发包中的显式锁,提供了更多的功能。
public class LockExample {
private final ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 业务代码
} finally {
lock.unlock();
}
}
}
12. `Semaphore`有什么作用?
Semaphore
是一个计数信号量,用于控制对资源的访问。
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
try {
// 访问资源
} finally {
semaphore.release();
}
13. Java Concurrency API中的`Lock`接口是什么?对比同步它有什么优势?
Lock
接口提供了比synchronized
更灵活的锁定机制。
Lock lock = new ReentrantLock();
lock.lock();
try {
// 业务代码
} finally {
lock.unlock();
}
14. `Hashtable`的`size()`方法中明明只有一条语句`return count`,为什么还要做同步?
为了保证线程安全,防止在多线程环境下,size()
方法的调用结果不正确。
15. `ConcurrentHashMap`的并发度是什么?
ConcurrentHashMap
的并发度是指它内部分段锁的数量,决定了它可以同时支持的线程并发数。
16. `ReentrantReadWriteLock`读写锁的使用?
ReentrantReadWriteLock
允许多个读操作同时进行,但写操作是独占的。
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();
readLock.lock();
try {
// 读操作
} finally {
readLock.unlock();
}
writeLock.lock();
try {
// 写操作
} finally {
writeLock.unlock();
}
17. `CyclicBarrier`和`CountDownLatch`的用法及区别?
CyclicBarrier
用于等待所有线程到达一个共同点,然后继续执行;CountDownLatch
用于等待一组事件发生。
CyclicBarrier barrier = new CyclicBarrier(2);
CountDownLatch latch = new CountDownLatch(1);
barrier.await();
latch.await();
18. `LockSupport`工具是什么?
LockSupport
提供最基本的线程阻塞和唤醒功能。
LockSupport.park();
LockSupport.unpark(thread);
19. `Condition`接口及其实现原理?
Condition
用于线程间的协调,允许一个或多个线程等待某个条件。
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
20. `Fork/Join`框架的理解?
Fork/Join
框架是一种用于并行计算的框架,它通过分而治之的方式将任务分解成更小的任务并行执行。
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new MyRecursiveTask());
21. `wait()`和`sleep()`的区别?
wait()
释放对象锁,允许其他线程进入同步块;sleep()
不释放对象锁。
22. 线程的五个状态(五种状态,创建、就绪、运行、阻塞和死亡)?
线程的五个状态包括:新建、就绪、运行、阻塞和死亡。
23. `start()`方法和`run()`方法的区别?
start()
方法用于启动新线程,run()
方法是线程执行的入口点。
24. `Runnable`接口和`Callable`接口的区别?
Runnable
返回void
,Callable
可以返回值和抛出异常。
25. `volatile`关键字的作用?
volatile
关键字确保变量的读写操作对所有线程都是可见的,并且禁止指令重排。
26. Java中如何获取到线程dump文件?
可以通过jstack
工具获取线程dump文件。
27. 线程和进程有什么区别?
进程是系统进行资源分配和调度的独立单位,线程是进程中的一个实体,是CPU调度和执行的单位。
28. 线程实现方式有几种(四种)?
线程可以通过继承Thread
类、实现Runnable
接口、实现Callable
接口和通过线程池来实现。
29. 高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?
根据业务特点选择合适的线程池类型,如FixedThreadPool
、CachedThreadPool
、ScheduledThreadPool
等。
30. 如果你提交任务时,线程池队列已满,这时会发生什么?
线程池会根据饱和策略来处理,如AbortPolicy
会抛出异常,CallerRunsPolicy
会在提交任务的线程中执行任务。
31. 锁的等级:方法锁、对象锁、类锁?
锁可以应用于方法、对象或类,分别对应方法锁、对象锁和类锁。
32. 如果同步块内的线程抛出异常会发生什么?
同步块内的线程抛出异常会导致锁被释放。
33. 并发编程(concurrency)并行编程(parallelism)有什么区别?
并发编程是指多个线程在同一个进程中同时运行,而并行编程是指多个进程同时运行。
34. 如何保证多线程下`i++`结果正确?
可以使用AtomicInteger
或synchronized
关键字保证i++
操作的原子性。
35. 一个线程如果出现了运行时异常会怎么样?
线程出现运行时异常会导致线程终止。
36. 如何在两个线程之间共享数据?
可以通过共享对象、volatile
关键字、ThreadLocal
等机制在两个线程之间共享数据。
37. 生产者消费者模型的作用是什么?
生产者消费者模型用于协调生产者和消费者之间的工作,确保生产者生产的数据能够被消费者及时处理。
38. 怎么唤醒一个阻塞的线程?
可以通过调用Object
的notify()
或notifyAll()
方法唤醒阻塞的线程。
39. Java中用到的线程调度算法是什么?
Java中使用的线程调度算法通常是抢占式调度算法。
40. 单例模式的线程安全性?
单例模式可以通过加锁或使用volatile
关键字保证线程安全。
41. 线程类的构造方法、静态块是被哪个线程调用的?
线程类的构造方法和静态块是由创建线程的线程调用的。
42. `ExecutorService`和`Executor`有什么区别?
ExecutorService
是Executor
的子接口,提供了提交任务和关闭线程池的方法。
43. `ThreadPoolExecutor`的工作原理是什么?
ThreadPoolExecutor
是一个线程池实现,它维护了一个线程队列和一组工作线程。
44. `Executor`框架中的`ThreadPoolExecutor`如何合理设置线程池大小?
线程池大小可以根据系统的CPU核心数和任务类型(CPU密集型或IO密集型)来设置。
45. `Executor`框架中的`ScheduledThreadPoolExecutor`的作用是什么?
ScheduledThreadPoolExecutor
用于安排任务在给定的延迟后运行,或者定期执行。
46. `Executor`框架中的`ForkJoinPool`的作用是什么?
ForkJoinPool
是一个专为分治任务设计的线程池,它通过工作窃取算法来提高效率。
47. `CompletableFuture`的工作原理是什么?
CompletableFuture
提供了一种异步编程的解决方案,它允许你以非阻塞的方式执行任务,并处理结果。
48. `ThreadLocal`的内存泄露问题?
ThreadLocal
变量如果没有正确清理,可能会导致内存泄露。
49. `Executor`框架中的`Executors`工厂方法创建的线程池有什么特点?
Executors
工厂方法创建的线程池有固定大小、可缓存、单线程和定时/周期性任务的线程池实现。
50. `Fork/Join`框架中的任务如何拆分?
Fork/Join
框架中的任务可以通过invokeAll
方法拆分成多个子任务并行执行。