一、HashMap和ConcurrentHashMap
HashMap底层源码分析
如何解决Hash碰撞的(延伸出Hash冲突的几种解决方法)
通过h & (table.length -1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方,这是HashMap在速度上的优化
当length总是2的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率
put过程
扩容机制
get过程
HashMap的初始化容量为什么是2的次幂
https://www.iteye.com/topic/539465
为什么HashMap长度大于8才转换为红黑树
HashMap和HashTable的区别?
https://www.cnblogs.com/williamjie/p/9099141.html
HashMap和TreeMap比较?
HashMap,HashTable,CourrentHashMap的key和value是否可为null?那为什么这么设计?
CourrentHashMap源码分析
二、JUC(java.utils.concurrent)
并发编程的的三个概念(特性)?为什么会有Volatile关键字?volatile关键字的两层语义!!volatile底层原理!!
原子性 ==> 不可分割、完整性,即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割,需要整体完整,要么同时成功,要么同时失败
可见性 ==> 当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值
有序性 ==> 为了提高性能,JVM在编译Java代码,或者CPU在执行JVM字节码的时候,对现有的指令顺序进行重新排序
单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致
处理器在进行重排顺是必须要考虑指令之间的数据依赖性
多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性时无法确定的,结果无法预测
volatile是java虚拟机提供的轻量级同步机制 ==> 保证可见性、不保证原子性、禁止指令重排
volatile底层原理
volatile的底层是通过lock前缀指令、内存屏障来实现的
https://www.jianshu.com/p/2643c9ea1b82
JMM(Java内存模型)
JMM(Java Memory Model)本身是一种抽象的概念,并不真实存在,他描述的是一组规则或规范。
通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。
JMM关于同步的规定:
1. 线程解锁前,必须把共享变量的值刷新回主内存
2. 线程加锁前,必须读取主内存的最新值到自己的工作内存
3. 加锁解锁时同一把锁
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有的成为栈空间),工作内存是每个线程的私有数据区域,而java内存模型中规定所有变量都存储在主内存,主内存
是贡献内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回
主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存的变量副本拷贝,因此不同的线程件无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。
期间要访问过程如下图:
在哪些地方用过volatile
双重检查锁机制(Double Check Lock)
步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变,
因此这种重排优化时允许的,如果3步骤提前于步骤2,但是instance还没有初始化完成
所以当一条线程访问instance不为null时,由于instance示例未必已初始化完成,也就造成了线程安全问题
基于volatile的解决方案
为解决以上问题,可以将SingletongDemo实例上加上volatile
private static volatile SingletonDemo instance = null;
什么是线程安全?产生线程不安全的原因是什么?Java线程安全的类?
在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存在被意外修改的风险
CopyOnWriteArrayList.add方法
CAS原理(CompareAndSwap 比较并交换)
以AtomicInteger为例
Unsafe
是CAS核心类,由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存数据。
Unsafe类存在于 sun.misc 包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法
Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务
CAS缺点
如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过
J.U.C包提供了一个带有标记的原子引用类 AtomicStampedReference来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性
公平锁/非公平锁;可重入锁;独享锁/共享锁;乐观锁/悲观锁;手写自旋锁
公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象
可重入锁(递归锁)
指的时同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁
也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块
ReentrantLock/Synchronized 就是一个典型的可重入锁
可重入锁最大的作用是避免死锁
ReentrantReadWriteLock源码分析
自旋锁
是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环
/** * 手写自旋锁 * @author */ public class SpinLock { AtomicReference<Thread> atomicReference = new AtomicReference<>(); public void lock() { Thread thread = Thread.currentThread(); while(!atomicReference.compareAndSet(null, thread)) { } } public void unlock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); } }
synchronized和ReentrantLock
synchronized三种应用方式;为什么能实现实现内存可见性;底层原理
synchronized修饰静态方法和成员方法的区别
ReentrantLock类的实现原理
synchronized和ReentrantLock的异同
阻塞队列
阻塞队列的用途
生产者消费者模式
线程池
消息中间件
阻塞队列的实现原理
使用通知模式实现 ==> 当生产者往满的队列里添加元素时会阻塞住生产者,当消费者消费了一个队列中的元素后,会通知生产者当前队列可用
线程池
线程池的好处
线程池任务执行流程!!
Java中的ThreadPoolExecutor类!线程池涉及到的参数!
线程池的状态?
https://blog.csdn.net/l_kanglin/article/details/57411851
任务缓存队列及排队策略,如何自定义拒绝策略?
线程池的种类?
配置线程池大小,根据CPU密集和IO密集划分
ThreadLocal介绍,实现原理!!ThreadLocal是如何做到为每一个线程维护变量的副本的呢?ThreadLocal和同步机制的区别
内存泄漏和内存溢出,常见的内存泄露(介绍一下HashMap泄露的场景),避免内存泄漏的几点建议?如何定位找到内存泄漏!!
设计模式的单例和工厂是面得最多的!单例的几种实现方式,一般写典型的双重检查锁定,因为会延伸出volatile,线程安全这些。
饿汉式,线程安全为什么不用这个方式呢,简单又线程安全?然后是抽象工厂模式和工厂方法模式区别?JDK或者Spring当中哪里用了设计模式?