1. Java中线程和进程的区别是什么?
- 进程是系统分配资源的基本单位,它包含了一个或多个线程以及相关的系统资源(如内存、文件句柄等)。进程间相互独立,通过进程间通信(IPC)进行信息交换。
- 线程是CPU调度的基本单位,它是进程内的一条执行路径。线程共享进程的资源,因此线程间通信相对简单,但也可能导致资源竞争和同步问题。
2. 什么是Java内存模型(JMM)?它在并发编程中有什么作用?
- Java内存模型定义了线程和主内存之间的抽象关系,以及线程之间共享变量的可见性和有序性规则。它解决了由于多线程并发访问共享数据可能导致的可见性、原子性和有序性问题。
3. volatile关键字的作用是什么?
- volatile关键字用于声明变量,保证了变量的可见性和禁止指令重排序。它主要用于多线程环境下的变量共享,确保一个线程对变量的修改对其他线程是可见的。
4. 什么是线程局部变量(ThreadLocal)?它在什么场景下使用?
- ThreadLocal提供了线程本地变量。这些变量与其他普通变量的区别在于,每一个访问这个变量的线程都有其自己独立初始化的变量副本。常用于解决多线程环境下,每个线程需要有自己的独立变量副本的问题,如数据库连接、用户身份信息等。
5. 什么是阻塞队列?它在Java并发包中是如何实现的?
- 阻塞队列是一种特殊的队列,当队列为空时,从队列中获取元素的线程将会被阻塞,直到队列中有新的元素可以获取;当队列已满时,试图往队列里添加新元素的线程也将被阻塞,直到队列有空余空间。Java的
java.util.concurrent
包中提供了多种阻塞队列的实现,如ArrayBlockingQueue
、LinkedBlockingQueue
等。
6. 什么是Future和Callable?它们在并发编程中有什么应用?
- Future和Callable是Java并发包中用于异步计算的两个接口。Callable用于定义需要异步执行的任务,并返回一个结果;Future用于表示异步计算的结果,提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。它们通常与ExecutorService一起使用,实现异步编程模型。
7. 什么是ForkJoinPool?它适用于哪些场景?
- ForkJoinPool是Java 7引入的一个用于执行可以拆分为子任务的任务的线程池。它适用于可以递归拆分为更小任务的问题,如大规模数据处理、并行计算等。通过合理地拆分和合并任务,ForkJoinPool可以有效地利用多核处理器,提高程序的执行效率。
8. 如何在Java中实现线程间的通信?
- Java中线程间通信的主要方式有:共享内存、消息队列、等待/通知机制(wait/notify/notifyAll)以及条件变量(Condition)等。这些机制可以帮助线程间协调执行顺序、共享数据以及处理并发问题。
9. 什么是CAS操作?它在Java并发中有什么应用?
- CAS(Compare and Swap)操作是一种无锁技术,它包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。当内存位置V的值等于预期原值A时,将内存位置V的值设置为新值B。否则,不做任何操作。CAS操作在Java并发中常用于实现无锁数据结构,如原子变量、并发集合等,以避免使用锁带来的性能开销。
10. Java中的原子类有哪些?它们是如何保证原子性的?
- Java中的原子类如
AtomicInteger
、AtomicLong
、AtomicReference
等,它们通过CAS操作来保证原子性。原子类中的方法如incrementAndGet()
、compareAndSet()
等内部使用了CAS操作,确保在多线程环境下对共享变量的操作是原子的。
11. 什么是AQS(AbstractQueuedSynchronizer)?它在Java并发包中是如何应用的?
- AQS是一个用于构建锁和同步器的框架,它使用了一个FIFO的队列来管理获取锁的线程。AQS提供了原子状态管理、阻塞和唤醒线程等功能,使得开发者可以方便地实现各种同步机制。Java并发包中的
ReentrantLock
、Semaphore
、CountDownLatch
等都是基于AQS实现的。
12. 什么是锁的顺序和锁偏序?如何避免死锁?
- 锁的顺序是指多个线程在尝试获取锁时遵循的特定顺序。如果所有线程都按照相同的顺序请求锁,那么可以避免出现循环等待的情况,从而避免死锁。锁偏序是指由于锁的获取顺序不一致导致的非确定性行为。为了避免死锁,可以采取以下策略:避免嵌套锁、按固定顺序获取锁、使用定时锁、检测死锁并恢复等。
13. Java中的锁策略有哪些?它们各自适用于什么场景?
- Java中的锁策略包括公平锁、非公平锁、可重入锁、读写锁等。公平锁按照线程请求锁的顺序来分配锁,适用于需要保证线程公平性的场景;非公平锁则不保证这个顺序,适用于追求性能的场景。可重入锁允许同一线程多次获取同一把锁,适用于递归调用的场景。读写锁则分为读锁和写锁,允许多个线程同时读取共享资源,但只允许一个线程写入,适用于读多写少的场景。
14. 什么是线程状态?Java中线程有哪些状态?
- 线程状态指的是线程在生命周期内所处的不同阶段。在Java中,线程有五种状态:新建(NEW)、就绪(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)和终止(TERMINATED)。这些状态反映了线程在执行过程中的不同情况,如等待资源、执行任务、被阻塞等。
15. 请解释Java中的Happens-Before规则。
- Happens-Before是Java内存模型定义的两项操作之间的偏序关系,如果两个操作之间存在Happens-Before关系,那么前一个操作的结果对后一个操作是可见的。这有助于我们理解多线程环境下变量可见性的保证。
16. 什么是Java的内存屏障?它如何保证并发安全性?
- 内存屏障是一组处理器指令,用于控制不同CPU或不同CPU核心之间的内存访问操作顺序。在Java中,可以通过volatile关键字引入内存屏障,从而确保volatile变量的读写操作的顺序性,进而保证并发安全性。
17. 如何使用Java进行性能调优,特别是在并发环境下?
- 在并发环境下,性能调优涉及多个方面,如减少线程上下文切换、优化锁策略、使用无锁数据结构、合理设置线程池大小等。此外,还可以使用JVM调优工具(如JProfiler、VisualVM等)来分析和优化程序的性能。
18. 简述Java中的分段锁(Segment Lock)及其应用场景。
- 分段锁是ConcurrentHashMap等并发容器中使用的一种锁策略。它将整个容器划分为多个段(Segment),每个段都有自己的锁。这样,多个线程可以同时访问不同段的数据,从而提高并发性能。适用于读多写少的并发场景。
19. 在Java中,如何检测和处理死锁?
- Java提供了jstack等工具来检测死锁。当检测到死锁时,需要分析线程栈信息,找出导致死锁的线程和锁,然后调整锁的顺序或避免嵌套锁来解决死锁问题。此外,也可以使用tryLock等方法来尝试获取锁,避免长时间等待导致的死锁。
20. Java中的锁升级是什么?为什么需要锁升级?
- 锁升级是指JVM在运行时将对象的锁从无锁状态逐步升级为偏向锁、轻量级锁和重量级锁的过程。这是为了优化性能,减少不必要的锁开销。当多个线程竞争同一个锁时,会逐步升级锁的级别,以保证线程安全。
21. 请谈谈你对Java并发编程中“分而治之”策略的理解。
- “分而治之”是并发编程中的一种重要思想,它将一个大任务拆分成多个小任务,然后让多个线程并行执行这些小任务。这样可以充分利用多核CPU的并行处理能力,提高程序的执行效率。在Java中,可以使用ForkJoinPool等并发工具来实现“分而治之”策略。