Java基础
- Java入门
- Java语法
- 基本数据类型
- 方法(函数)
- 类和对象
- 面向对象三大特征
- 修饰符
- 接口和抽象类
- 其他重要知识点(BigDecimal/Arrays.asList等)
- 集合
- 异常
- 多线程
- 文件与I/O流
- 枚举
- Java 常见关键字总结:final、static、this、super
- 什么是反射机制?反射机制的应用场景有哪些?
- 参考
Java基础知识
Java 教程
Java8 Lambda表达式详解手册及实例
JAVA8 STREAM新特性详解及实战
如何在Java中将int []转换为Integer []?
Java8 方式解决Stream流转其他数组
idea中java程序打jar包的两种方式(超详细)
Java疑难点
为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较?
Java容器
- Java集合概述
- 说说 List,Set,Map 三者的区别?
- 如何选用集合?
- Iterator 迭代器
- 有哪些集合是线程不安全的?怎么解决呢?
- Arraylist 和 Vector 的区别?
- Arraylist 与 LinkedList 区别?
- 说一说 ArrayList 的扩容机制吧
- ArrayList如何进行数组拷贝,深拷贝和浅拷贝
- Comparable 和 Comparator 的区别
- 比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
- HashMap 和 Hashtable 的区别
- HashMap 和 HashSet 区别
- HashMap 和 TreeMap 区别
- HashSet 如何检查重复
- HashMap 的底层实现(JDK1.8 之前 / JDK1.8 之后)
- HashMap 的长度为什么是 2 的幂次方
- HashMap 多线程操作导致死循环问题
- HashMap 有哪几种常见的遍历方式?
- ConcurrentHashMap 和 Hashtable 的区别
- ConcurrentHashMap 线程安全的具体实现方式/底层具体实现(JDK1.8 之前 / JDK1.8 之后)
- Collections 工具类排序操作
- Collections 工具类查找、替换操作
- Collections 工具类同步控制
- 快速失败(fail-fast)
- 安全失败(fail-safe)
- ArrayList 源码
- LinkedList 源码
- HashMap(JDK1.8)源码
- ConcurrentHashMap源码
- 参考
Java集合框架常见面试题
Java 集合框架
ArrayList的深拷贝与浅拷贝
ArrayList clone()– ArrayList深拷贝和浅拷贝
并发
Java并发编程之美(2018)
- 线程创建的三种方式
- 线程notify() / notifyAll() / wait() 方法
- 线程的join() 方法
- 线程的sleep() 方法
- sleep()和wait()的区别
区别是:
1. sleep()不释放同步锁,wait()释放同步锁
2. sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到你只能调用interreput()来强行打断;wait()可以用notify()直接唤起.
3. 这两个方法来自不同的类分别是Thread和Object
4. wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
synchronized(x){ x.notify() //或者wait() }
5. sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
-
线程的yield() 方法
-
线程中断 / interrupt()方法;InterruptedException异常;通过interrupt优雅地退出一个线程
-
线程死锁,避免死锁
-
守护线程;setDaemon(true);JVM等待所有线程退出后才结束;jps命令的使用
-
ThreadLocal类 / InheritableThreadLocal类
-
如何引起线程安全问题;读取-修改-写入操作
-
Java多线程的内存模型
-
Java共享变量的内存可见性问题;定义;如何产生;如何解决
-
synchronized关键字;synchronized关键字在内存模型中的内存语义;如何解决共享内存不可见和原子性问题
synchronized(修饰方法和代码块) -
volatile关键字;如何解决共享内存不可见问题,无法解决原子性问题
-
指令重排和volatile关键字
-
Java中的原子性和CAS操作
-
CAS操作和独占锁在实现原子性上开销的差距;java-cas与锁比较
-
Unsafe类(主要包含CAS操作,无法普通调用)
-
伪共享;定义,结合多线程内存模型;多线程和单线程条件下的区别;如何避免
-
乐观锁和悲观锁
-
公平锁和非公平锁;ReentrantLock
-
独占锁和共享锁;ReadWriteLock
-
可重入锁;synchronized实例;解释代码;原理实现
Synchronized可重入锁分析 -
自旋锁;CPU占用和内核切换的权衡
-
Random类;nextInt方法迭代种子;多线程产生随机数时出现的重复问题
-
利用AtomicLong原子CAS操作实现,不停轮询,CPU占用高
-
ThreadLocalRandom,类似ThreadLocal实现,每个线程自己维护一个seed
-
current()方法和nextInt()方法
-
AtomicLong类如何实现内存可见和原子性;
-
LongAdder类对AtomicLong类的改进;Cell子类的结构;add() / reset() / sum()
-
LongAccumulator类;accumulate() / longValue()
-
CopyOnWriteArrayList类;使用了Arrays.copyOf()方法获得数组拷贝;setArray()方法覆盖原有数组
-
CopyOnWriteArrayList类弱一致性的迭代器;即迭代器传递的是数组引用,但是如果在修改线程start之前创建迭代器,一切增删改操作都无法在迭代器生效
-
LockSupport类;基类;park()方法和unpark()方法
-
抽象同步队列AQS;双向队列,状态量state,条件变量
-
生产者-消费者模型
-
独占锁ReentrantLock;state与可重入机制
-
lock()方法以及如何实现①返回false加入AQS阻塞队列;②state为0;③当前线程为锁持有者时可重入;④公平锁与非公平锁
-
unlock()方法以及state值①为0时释放锁②对可重入锁state值减1
-
条件队列与阻塞队列Node节点的轮换;条件变量的await()和signal();
-
读写锁ReentrantReadWriteLock;state通过位运算获得前16位作为读锁,后16位作为写锁
-
写锁的lock()方法,①state值不为0,此时为读锁或者非当前线程则返回false添加进AQS阻塞队列,当为可重入写操作时加1;②state为0
-
写锁的unlock()方法,①可重入次数减1②判断可重入次数是否为0,当为0则释放锁且激活阻塞队列的一个线程
-
读锁的lock()方法,①如果写锁被占用且非当前线程则放入AQS阻塞队列(即当前为写锁依然能读)②增加读锁的计数count
-
并发队列;使用锁实现阻塞队列,使用CAS实现非阻塞队列
-
ConcurrentLinkedQueue实现线程安全的无界非阻塞队列;底层使用单向链表,head、tail节点都设置为volatile保证内存可见性,入队使用casNext的CAS操作,出队使用CAS操作将头部节点设置为null并重新设置头节点后移除该节点
-
LinkedBlockingQueue使用独占锁实现有界阻塞队列;底层使用单向链表,使用AtomicInteger计算队列元素个数,拥有两个锁takeLock和putLock分别控制出队入队的原子性以及两个条件变量notEmpty和notFull用来存放阻塞的线程,实现生产者-消费者模型;
-
offer()方法非阻塞,使用putLock锁保证入队的原子性,随后根据队列中元素个数多少,进行notFull和notEmpty的signal(),注意调用条件变量的方法需要获取对应的锁;put()方法在队列已满时需要调用notFull的await()方法把线程放入条件队列,并释放putLock锁,判断await()需要在while (临界条件)下,为了避免出现虚假唤醒。
-
poll()方法非阻塞,使用takeLock锁保证出队的原子性,并递减计数器,随后根据队列中元素个数多少,进行notEmpty和notFull的signal();take()方法在队列为空时需要调用notEmpty的await()方法把线程放入条件队列,并释放takeLock锁
-
remove()方法删除队列的指定元素,需要双重加锁,通过遍历队列来删除,注意解锁顺序应与加锁顺序相反
-
ArrayBlockingQueue使用独占锁实现线程安全的有界阻塞队列;底层使用有界数组,拥有一个锁lock保证出入队的原子性,这也使得同一时间只有一个线程能进行出入队操作,拥有两个条件变量notEmpty和notFull用来存放阻塞的线程
-
offer()/poll()和put()/take()与LinkedBlockingQueue类似
-
PriorityBlockingQueue实现线程安全的带优先级的无界阻塞队列,底层使用堆数组实现,需要对象满足Comparable接口,包含一个自旋锁allocationSpinLock,其值为{0, 1},使用CAS保证只有一个线程可以扩充队列,包含一个独占锁lock控制出入队的原子性,只有notEmpty条件变量没有notFull(无界)
-
offer()方法非阻塞,使用lock保证入队的原子性,如果当前元素个数大于capacity需要进行扩容(无界性保证),tryGrow()方法先释放锁使得其他出入队线程可以获得锁,然后判断并使用CAS设置自旋锁的值,得到扩容后的新数组,再获取锁后将原数组拷贝至新数组,注意对于其他未能获得自旋锁进行扩容的线程,将一直while原地自旋等待扩容完成,扩容完成后使用堆操作入队,计数器加1,并通知notEmpty条件队列
-
poll()方法非阻塞,需要获取lock锁保证出队的原子性,使用堆操作弹出堆顶元素
-
put()方法仍然是非阻塞的(无界),take()方法在队列为空时需要阻塞放入条件队列
-
DelayQueue实现线程安全的带延迟时间的无界阻塞队列,内部基于PriorityBlockingQueue实现,其中元素需要实现继承了Comparable接口的Delayed接口,poll()/take()方法用于移除其中的过期元素
-
使用线程池的好处
-
ThreadPoolExecutor,工厂方法产生newCachedThreadPool, newFixedThreadPool, newSingleThreadExecutor
-
成员ctl原子变量同时记录线程池状态(种类)和当前线程个数;workQueue作为线程的阻塞队列;mainLock独占锁提供阻塞队列出入队的原子性,termination为条件变量;加入线程池的任务包装为Worker,自身实现了不可重入的独占锁,state值为{0, 1}
-
execute()方法;作用是提交任务到线程池中运行;如果当前线程个数小于核心线程池个数,则进行addWorker()方法提交任务运行,如果当前线程个数大于等于核心线程池个数,且处于Running状态,则移入阻塞队列,如果此时阻塞队列已满,则执行拒绝策略。addWorker()方法首先判断线程池的状态,然后使用CAS操作增加一个线程,随后创建一个Worker对象,将新任务添加进线程池并执行
-
shutdown()方法;先进行权限检查,再设置线程池的状态,然后为所有空闲线程设置为interrupt(为所有未中断且Worker对象无锁),注意interrupt()需要先获得Worker锁
-
shutdownNow()方法;与shutdown()不同的是,需要中断空闲线程和正在执行任务的线程
-
awaitTermination()方法;获取锁后循环询问是否到达TERMINATED状态,等待时间超时后才返回
-
CountDownLatch;使用countDown()和await()控制多个线程的同步,注意需要提前指定state的个数,state为0则继续执行。CountDownLatch和WaitGroup
-
CyclicBarrier;相比于CountDownLatch,多了count和parties,当一轮结束完成后执行预设任务再重置,通过await()方法将线程在AQS阻塞队列和trip条件队列之间转换,适用于分段任务有序执行的场景
-
Semaphore信号量;相比于CountDownLatch,无需提前指定state值,通过release()增加信号量,在aquire()方法处比较已有信号量和所需信号量,小于0则阻塞当前线程,唤醒后重新进入acquire()流程
Java并发面试知识点
-
Java中线程的生命周期以及转换
-
为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
-
synchronized关键字的三种修饰方式
-
双重校验锁实现单例模式
-
synchronized关键字实现功能的底层原理
-
synchronized关键字底层轻量级的优化
-
synchronized与ReentrantLock的区别
-
volatile关键字的两个作用
-
CPU缓存模型和JMM模型
-
并发编程需要满足的三个特性(原子性、内存可见性、有序性)
-
ThreadLocal及ThreadLocalMap原理
-
为什么要使用线程池?
-
Runnable和Callable接口的区别
-
示例代码:Runnable/Callable+ThreadPoolExecutor
-
线程池execute()方法和submit()方法以及Future接口及get()阻塞方法
-
线程池shutdown()方法和shutdownNow()方法
-
ThreadPoolExecutor的构造函数的参数
-
ThreadPoolExecutor的饱和策略
-
线程池添加任务的流程图
-
FixedThreadPool / SingleThreadExecutor / CachedThreadPool 构造、执行任务过程、OOM可能性
-
CPU密集型和I/O密集型任务线程池大小的确定(N+1 / 2N)
-
JUC包中的4种原子类(AtomicInteger等)
-
AtomicInteger类的使用/ConcurrentLinkedQueue的使用
-
AtomicInteger如何实现线程安全(CAS+volatile)
-
AtomicIntegerArray类的使用
-
AtomicReference类的使用
-
AQS的原理
-
AQS的常用组件(Semaphore/CountDownLatch/CyclicBarrier)及使用
-
乐观锁和悲观锁的概念及应用场景
-
乐观锁的两种常见实现方式(版本号、CAS)
-
乐观锁的三个缺点(ABA问题、自旋开销、JDK1.5之前只能保证一个共享变量(AtomicReference))
-
CAS与传统锁的应用场景
-
synchronized关键字JDK1.6之后的改进(自旋后阻塞,竞争切换后继续竞争锁);在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS
-
Java常用并发容器的使用(api与非线程安全的基本相同)和原理
-
ConcurrentHashMap;线程安全的HashMap,ConcurrentHashMap与HashTable的区别,以及JDK1.7的分段锁实现和JDK1.8的节点锁的实现
-
CopyOnWriteArrayList;适用于读多写少的ArrayList,且读写不会冲突,读操作不上锁,写操作通过copy一个容量+1的数组来实现扩容
-
ConcurrentLinkedQueue;非阻塞队列,由于使用了CAS算法进行offer等操作,所以性能很高,但是冲突很强的时候也会碰到CAS一样的问题
-
BlockingQueue由于使用了锁和条件变量,所以是阻塞队列,可实现"生产者-消费者"问题
- ArrayBlockingQueue;实现上述接口的有界队列,一旦创建数组底层不可改变,注意出入队都只能获取同一个锁
- LinkedBlockingQueue;基于单向链表实现,原则上是无界队列,也可以限制为有界队列,出入队由不同的锁控制
- PriorityBlockingQueue;基于堆的结构实现,无界队列,没有notFull条件变量,可以自动扩容
-
ConcurrentSkipListMap;使用跳表(有序链表)以空间换时间的方式实现Map接口,能以O(logN)获取元素,插入删除元素,同时使用分段锁达到不同分链表快速并行,同时,所有元素也是有序的
JVM
-
Java的内存区域是什么样子,以及JDK1.8的变化
-
内存区域中各部分的作用
-
Java对象是如何创建的
-
一个主类中执行某方法执行时的JVM内部过程
-
Java对象在内存中的布局(对象头、实例数据和对齐填充)
-
Java对象的访问定位
-
String类和常量池技术,字符串拼接带来的问题
-
Integer的“==”比较
-
JVM中堆空间的基本结构
-
对象如何经历Minor GC从Eden区→Survivor区(from/to)→老年区
-
新生代晋升到老年代的动态年龄
-
Partial GC和Full GC
-
判断对象是否失效(引用计数法、可达性分析法)
-
垃圾收集算法(标记-清除、标记-整理、复制算法、分代收集策略)
-
垃圾收集器
-
重点了解ParNew/CMS/G1
——ParNew,常用于新生代中如Eden区和Survivor区之间的移动
——CMS,如老年区的并发标记清除
- 常用监控命令行(jps、jstat、jinfo、jmap、jhat、jstack)
- 可视化插件Visual VM
- 类的加载过程(加载、验证、准备、解析、初始化)以及类的卸载
- 类加载器内置有哪三种(启动类加载器、扩展类加载器、应用程序类加载器)
- 双亲委派模型
- 双亲委派模型好处(避免类重复加载、保证核心类不被修改)
- JVM参数及调优(堆内存相关参数、垃圾收集相关参数、GC记录相关)
- GC持续时间长,GC频繁怎么排除
- Full GC的Remark阶段持续时间长怎么排除
- 参考
JavaGuide/docs/java/jvm/
[JVM] IDEA集成VisualVM
jvm可视化工具插件—Visual GC
Java性能调优:利用VisualVM进行性能分析
JVM性能调优详解
从实际案例聊聊Java应用的GC优化
美团技术团队