Java基础知识
Java集合
HashMap
1.为什么map的长度是2的n次幂?
参考:https://carsonho.blog.csdn.net/article/details/79373134
- 更高的运算效率:HashMap的元素下标计算方式:h & (length - 1),如果length为2的n次幂,那么length-1的二进制值全是1,只会保留h值的低n位。简单说就是对哈希值按length长度取模,但是 & 运算比 % 运算效率更高
- 均匀分布:哈希码的计算方式加了二次扰动后才计算下标的,即哈希码的 h ^ (h >>>16),因为低位数有限,容易冲突,让高位参与运算,减少冲突用的
HashTable
Hashtable之所以效率低下主要是因为其实现使用了synchronized关键字对put等操作进行加锁,而synchronized关键字加锁是对整个对象进行加锁,也就是说在进行put等修改Hash表的操作时,锁住了整个Hash表,从而使得其表现的效率低下。
JUC常用并发容器
ConcurrentHashMap
- ConcurrentHashMap的原理是什么?如何实现线程安全?
- 数据结构与HashMap基本一致:数组+链表+红黑树,用CAS和sychronized来加锁
- 扩容一次只扩一个桶,这样可以减少扩容造成的并发冲突
- 链表长度达到一定阈值,默认8,链表转为红黑树
- CopyOnWriteArrayList的使用场景是什么?它是如何实现线程安全的?
- BlockingQueue有哪些实现?它们之间的区别是什么?
- 使用ConcurrentLinkedQueue和LinkedList有什么区别?
AQS(AbstractQueuedSynchronizer)原理
- 简要解释一下AQS的工作原理。
- AQS使用一个volatile修饰的变量表示同步状态,使用CAS操作实现对同步状态的修改
- 虚拟双向队列 CLH队列:一个线程获取不到锁,就封装为队列的一个Node加入等待队列中
- 资源获取方式:独占、共享,独占即只有一个线程能执行,如ReentrantLock。共享是多个线程可执行,如CountDownLatch
- 独占方式有2种锁:公平锁和非公平锁,公平锁:先到先得,非公平锁:抢锁,谁抢到就是谁的
- acquire方法: 在独占模式下,acquire方法用于获取同步状态,即抢锁
- release方法: 在独占模式下,release方法用于释放同步状态
sychronized原理(监视器)
monitorenter指令:monitor计数器为0时,线程就会立刻获得然后把锁计数器+1,别的线程只能等待锁
monitorexit指令:释放对monitor的所有权,就是计数器减1,
可重入性: 当一个线程已经获得了某个对象的锁,它再次尝试获取该对象的锁时,会成功。这个特性叫做锁的重入性,计数器会累加
总结:
任意线程对Object的访问,首先要获得Object的监视器,如果获取失败,该线程就进入同步状态,线程状态变为BLOCKED,当Object的监视器占有者释放后,在同步队列中得线程就会有机会重新获取该监视器。
- ReentrantLock和ReentrantReadWriteLock是如何使用AQS的?
线程池
- ThreadPoolExecutor的核心参数有哪些?它们的作用是什么?
- 什么是工作队列(work queue)?ThreadPoolExecutor中的工作队列有哪些类型?
- CachedThreadPool和FixedThreadPool有什么区别?
- 讲解一下线程池的生命周期。
线程通信和CountDownLatch
- 什么是线程通信?如何实现线程间通信?
- CountDownLatch的作用是什么?它是如何工作的?
- 如何使用CountDownLatch来实现主线程等待所有子线程完成后再继续执行?
其他
- 如何避免死锁?谈谈你对死锁的理解。
- 什么是乐观锁和悲观锁?分别举例说明。
- 简要解释一下CAS(Compare and Swap)操作。
JVM
- JVM的架构是什么样子的?简要描述一下主要组件。
- Java程序是如何在JVM中运行的?
- 解释一下JIT编译器的作用。
内存模型
- Java内存模型是什么?为什么它是重要的?
- 什么是堆(Heap)和栈(Stack)?它们之间有什么区别?
- 解释一下Java的方法区(Method Area)和永久代(Permanent Generation)(在Java 8及之前)或元空间(Metaspace)(在Java 8之后)。
类加载机制
- Java的类加载过程是怎样的?
包括五个步骤:加载、验证、准备、解析、初始化
- 加载:类加载器负责查找字节码文件,并将其加载到内存中。启动类加载器(Bootstrap Class Loader),扩展类加载器,应用程序类加载器
- 验证:确保被加载的类是符合Java虚拟机规范的
- 准备:Java虚拟机为类的静态变量分配内存并设置默认初始值,如将数字类型初始化为0,对象类型初始化为null。
- 解析:将类、字段、方法的符号引用解析为直接引用
- 初始化:类的静态变量被初始化为程序中给定的值
- 解释一下类加载器的层次结构和作用。
- 什么是双亲委派模型?它在类加载中有什么作用?
- 一个类加载器收到加载请求时,先检查是否已经加载过这个类了,如果加载过,那么直接返回类的Class对象
未加载,则将其委派给父加载器,父加载器进行相同的检查,层层委派到启动类加载器,如果父加载器可以加载,那么返回Class对象。否则子加载器尝试加载该类
好处:避免不同加载器对Java核心类的重复加载,确保Java核心库的安全性。避免冲突:通过这个模型,每个类只会被加载一次,避免了类冲突的问题。
垃圾收集算法和回收器
- 常见的垃圾收集算法有哪些?简要描述它们的原理。
- 标记-清除算法:可达性分析标记所有存活对象,然后清理回收未被标记的对象。容易产生内存碎片
- 标记整理算法:可达性分析标记所有存活对象,然后将所有对象往来一场移动,再清理边界外的内存。会带来性能开销,但是可解决碎片问题
- 复制算法:内存分为2个区域,每次只用其中一个区域,GC时将所有存活对象复制到另一块区域,再清空原来的区域。缺点不适用于大对象
- 分代收集:将堆分为新生代和老年代,采用不同的GC算法
- 常见的GC回收器有哪些?它们的特点和适用场景是什么?
- Serial收集器:单线程收集器,简单效率低,串行方式
- Parallel收集器:多线程收集器,以多线程方式对新生代进行垃圾收集,
- CMS收集器:
- 初始标记:标记GC Root直接关联的对象,暂停所有应用线程
- 并发标记:标记从GC Roots可达的所有对象,这个阶段和应用线程是并发的,
- 重新标记:为处理标记过程中产生的新垃圾,需要重新标记所有对象,会短暂停顿
- 并发清除: 并发清除未标记的对象
- 缺点:用的标记清除算法,存在内存碎片问题
- G1收集器:将内存划分为多个小空间Region,每个Region可单独回收,步骤:
- 初始标记、
- 并发标记、
- 最终标记:修正并发标记期间产生变动的记录,需停顿线程
- 筛选回收,根据用户期望的GC停顿时间和成本进行排序,只回收一部分Region
性能调优
- 如何进行JVM性能调优?
- 什么是GC暂停时间?如何减少GC暂停时间?
- 介绍一下常见的JVM性能分析工具。