对比 synchronized 和 volatile
并发编程需要从三个方面考虑线程安全,分别是:原子性、可见性、有序性
- volatile 修饰共享变量,可以保证它的可见性和有序性,但不能保证原子性(JMM模型)
- synchronized 代码块,不仅能保证共享变量的可见性、有序性,同时也能保证原子性
对比 synchronized 和 Lock
- synchronized 是关键字,Lock 是 Java 接口
- 前者底层是 C++ 代码实现锁,后者是 Java 自己的代码来实现锁
- Lock 功能更多,比如可以选择是公平锁还是非公平锁、可以设置加锁超时时间、可打断等
- Lock 的提供多种扩展实现(例如读写锁),可以根据场景选择更合适的实现
- Lock 释放锁需要调用 unlock 方法,而 synchronzied 在代码块结束无需显式调用就可以释放锁
线程池的核心参数
记忆七个参数
- 核心线程数
- 核心线程会常驻线程池
- 最大线程数
- 如果同时执行的任务数超过了核心线程数,且队列已满,会创建新的线程来救急
- 总线程数(新线程+原有的核心线程)不超这个最大线程数
- 存活时间
- 超过核心线程数的线程一旦闲下来,会存活一段时间,然后被销毁
- 存活时间单位
- 工作队列
- 如果同时执行的任务数超过了核心线程数,会把暂时无法处理的任务放入此队列
- 线程工厂
- 可以控制池中线程的命名规则,是否是守护线程等(不太重要的参数)
- 拒绝策略,队列放满任务,且所有线程都被占用,再来新任务,就会有问题,此时有四种拒绝策略:
- AbortPolicy 报错策略,直接抛异常
- CallerRunsPolicy 推脱策略,线程池不执行任务,推脱给任务提交线程
- DiscardOldestPolicy 抛弃最老任务策略,把队列中最早的任务抛弃,新任务加入队列等待
- DiscardPolicy 抛弃策略,直接把新任务抛弃不执行
JVM 堆内存结构
堆内存的布局与垃圾回收器有关。
传统的垃圾回收器会把堆内存划分为:老年代和年轻代,年轻代又分为
- 伊甸园 Eden
- 幸存区 S0,S1
如果是 G1 垃圾回收器,会把内存划分为一个个的 Region,每个 Region 都可以充当
- 伊甸园
- 幸存区
- 老年代
- 巨型对象区
垃圾回收算法
记忆三种:
- 标记-清除算法。优点是回收速度快,但会产生内存碎片
- 标记-整理算法。相对清除算法,不会有内存碎片,当然速度会慢一些
- 标记-复制算法。将内存划分为大小相等的两个区域 S0 和 S1
- S0 的职责用来存储对象,S1 始终保持空闲
- 垃圾回收时,只需要扫描 S0 的存活对象,把它们复制到 S1 区域,然后把 S0 整个清空,最后二者互换职责即可
- 不会有内存碎片,特别适合存活对象很少时(因为此时复制工作少)
Lambda表达式
什么是 Lambda 表达式
- 文献中把 Lambda 表达式一般称作匿名函数,语法为 (参数部分) -> 表达式部分
- 它本质上是一个函数对象
- 它可以用在那些需要将行为参数化的场景,例如 Stream API,MyBatisPlus 的 QueryWrapper 等地方
Lambda 与匿名内部类有何异同
- 它们都可以用于需要行为参数化的场景
- Lambda 表达式必须配合函数式接口使用,而匿名内部类不必拘泥于函数式接口,其它接口和抽象类也可以
- Lambda 表达式比匿名内部类语法上更加简洁
- 匿名内部类是在编译阶段由程序员编写提供,而 Lambda 表达式是在运行阶段动态生成它所需的类
- 【进阶】Lambda 中 this 含义与匿名内部类中的 this 不同