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是一个实现了RunnableFuture接口的类,用于包装Callable任务。

10. 什么是同步容器和并发容器的实现?

同步容器是指通过同步机制保证线程安全的容器,如VectorHashtable;并发容器是指通过并发控制机制保证线程安全的容器,如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返回voidCallable可以返回值和抛出异常。

25. `volatile`关键字的作用?

volatile关键字确保变量的读写操作对所有线程都是可见的,并且禁止指令重排。

26. Java中如何获取到线程dump文件?

可以通过jstack工具获取线程dump文件。

27. 线程和进程有什么区别?

进程是系统进行资源分配和调度的独立单位,线程是进程中的一个实体,是CPU调度和执行的单位。

28. 线程实现方式有几种(四种)?

线程可以通过继承Thread类、实现Runnable接口、实现Callable接口和通过线程池来实现。

29. 高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?

根据业务特点选择合适的线程池类型,如FixedThreadPoolCachedThreadPoolScheduledThreadPool等。

30. 如果你提交任务时,线程池队列已满,这时会发生什么?

线程池会根据饱和策略来处理,如AbortPolicy会抛出异常,CallerRunsPolicy会在提交任务的线程中执行任务。

31. 锁的等级:方法锁、对象锁、类锁?

锁可以应用于方法、对象或类,分别对应方法锁、对象锁和类锁。

32. 如果同步块内的线程抛出异常会发生什么?

同步块内的线程抛出异常会导致锁被释放。

33. 并发编程(concurrency)并行编程(parallelism)有什么区别?

并发编程是指多个线程在同一个进程中同时运行,而并行编程是指多个进程同时运行。

34. 如何保证多线程下`i++`结果正确?

可以使用AtomicIntegersynchronized关键字保证i++操作的原子性。

35. 一个线程如果出现了运行时异常会怎么样?

线程出现运行时异常会导致线程终止。

36. 如何在两个线程之间共享数据?

可以通过共享对象、volatile关键字、ThreadLocal等机制在两个线程之间共享数据。

37. 生产者消费者模型的作用是什么?

生产者消费者模型用于协调生产者和消费者之间的工作,确保生产者生产的数据能够被消费者及时处理。

38. 怎么唤醒一个阻塞的线程?

可以通过调用Objectnotify()notifyAll()方法唤醒阻塞的线程。

39. Java中用到的线程调度算法是什么?

Java中使用的线程调度算法通常是抢占式调度算法。

40. 单例模式的线程安全性?

单例模式可以通过加锁或使用volatile关键字保证线程安全。

41. 线程类的构造方法、静态块是被哪个线程调用的?

线程类的构造方法和静态块是由创建线程的线程调用的。

42. `ExecutorService`和`Executor`有什么区别?

ExecutorServiceExecutor的子接口,提供了提交任务和关闭线程池的方法。

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方法拆分成多个子任务并行执行。