近期面试各家互联网大厂、金融基金、证券、车企等公司,总结了一下相关面试笔记,内容概括性的,应对面试,深度和广度需要各位自行研究,面试时候如遇到此类问题,核心可参考以下解答,如果铺开和阐述看个人沟通能力!
JVM
java内存模型和系统内存模型
系统内存模型包括:代码段、方法区、堆
java内存模型即使java进程模型,进程中包括共享区域和线程独占区域
共享区域中包括:java堆(存放实例化的对象实例,垃圾回收操作区域)、方法区(存放数据加载的相关信息,包括常量、静态参数、入口)
线程独占区域包括:java虚拟机栈(类型栈帧(每个方法的执行都会生成一个栈帧),局部变量表-编译的时候已经分配好内存空间,不会改变)、本地方法区、程序计数器
垃圾回收:采用引用计数法(对象中添加引用计数器,对对象的引用进行计数)和可达性分析法对垃圾进行标记;
回收策略:标记清除、复制、标记整理、分代收集;回收器:serial、Parnew、G1
垃圾回收器
Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;
新生代收集器:Serial(复制)、ParNew、Parallel Scavenge(复制);
老年代收集器:Serial Old(标记-整理)、Parallel Old(标记-整理)、CMS(标记-清除);
整堆收集器:G1(整体:标记-整理;局部:复制)
GC Root
堆和方法是进程存储,用于线程共享,jvm虚拟栈、程序计数器、本地方法栈是线程独有,栈中本地变量,方法区中静态变量,本地方法栈变量,正在运行的线程可以作为GC Root(GC root是只有被引用而未引用)
gc时STW(stop the work)
不暂停应用的时候,存在垃圾回收状态不停变化的情况,到时垃圾回收不彻底
线程
线程:Runnable、Thread、Callable
run()和start()调用最大区别是线程数不一样
1---new Thread().start();
2---New Thread(new Runnable()).start();
3---FatureTask f=new FatureTask(new Callable());
New Thread(new FatureTask()).start();
f.get()
线程池
线程池有点:1避免线程池创建和销毁;2避免线程间资源抢夺导致阻塞;3提供线程定时执行,间隔执行功能
线程池中submit和execute区别:1submit有返回值,execute无返回值;2
Executor执行接口(execute);ExecutorService提交接口(submit,实现了返回值);AbstractExecutorService执行和提交整合合并;ThreadPoolExecutor(普通线程池类)
线程类型
newSingleThreadExecutor
锁
reentranctlock和synchronized
1.底层实现
Synchronized是JVM层面的锁,是Java关键字,通过monitor对象来完成。
ReentranLock是API层面的锁底层使用AQS。
2.释放锁
synchronized不需要用户手动释放锁。
ReentranLock可以手动释放锁,一般通过lock和unlock方法配合try/finally语句。
3.中断
synchronized是不可中断类型的锁
Reentranlock则可以中断,可通过tryLock(long timeout,TimeUnit unit)设置超时方法
4.公平锁
synchronized为非公平锁
ReetranLock默非公平锁,可以new ReentrantLock(true),true:公平锁 false:非公平锁
5.绑定条件Condition
synchronized不能绑定
ReentrantLock通过绑定Condition结合await()/singal()方法实现线程的精确唤醒,而不是像synchronized通过object类的wait()/notify()/notifyAll()方法要随机唤醒一个线程要么唤醒全部线程。
6.锁的对象
synchronzied锁的是对象,锁是保存在对象头里面,根据对象头数据来标识是否有线程获得锁/争抢锁。
ReetrantLock锁是线程,根据进入的线程和int类型的state标识锁的获得/争抢
分布式锁实现方式
①数据库层-->乐观锁(version即cas);悲观锁(基于数据库级别的for update)
②基于redis原子操作-->setnx+expire实现
③基于zookeeper-->基于interProcessMutex实现(分布式的公平可重入互斥锁,类似于单个JVM进程内的ReentrantLock(fair=true),利用临时顺序节点的顺序性)
zookeeper的节点有4中类型:永久节点,永久顺序节点,临时节点,临时顺序节点
④redisson是对redis进行封装的客户端,实现了很多锁和其他相关功能,其中分布式锁redLock实现原理:
redLock核心思想是n个节点(相互独立)中的n/2+1个节点获取到锁,切节点获取锁的时间远小于失效时间,才算获取锁成功,如果因为某些原因获取锁失败,则所有节点都需要进行解锁操作(包括为获取到锁的节点)
1.基于数据库
2.基于redis实现分布式锁
2.1:setnx+expire
缺点:获取锁是非阻塞,非公平锁,不支持需要公平锁的场景,redis主从存在延迟,在master宕机发生主从切换时,可能会导致锁失效
2.2:基于Redlock算法实现分布式锁。
在Redis的分布式环境中,我们假设有N个Redis master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制。我们确保将在N个实例上使用与在Redis单实例下相同方法获取和释放锁。
需要在所有节点都释放锁就行,不管之前有没有在该节点获取锁成功。
3.基于zookeeper实现分布式锁
利用临时节点与 watch 机制。每个锁占用一个普通节点 /lock,当需要获取锁时在 /lock 目录下创建一个临时节点,创建成功则表示获取锁成功,失败则 watch/lock 节点,有删除操作后再去争锁。
临时节点好处在于当进程挂掉后能自动上锁的节点自动删除即取消锁。
缺点:所有取锁失败的进程都监听父节点,很容易发生羊群效应,即当释放锁后所有等待进程一起来创建节点,并发量很大。
分布式事务(2pc、3pc、TCC)
2pc是一个非常经典的强一致、中心化的原子提交协议。这里所说的中心化是指协议中有两类节点:一个是中心化协调者节点(coordinator)和N个参与者节点(partcipant)。
第一个阶段为请求/表决阶段,第二阶段:提交/执行阶段(正常流程)
缺点:性能问题。执行过程,节点都处于阻塞状态。
协调者单点故障问题。事务协调者是整个XA模型的核心,一旦事务协调者节点挂掉,会导致参与者收不到提交或回滚的通知,从而导致参与者节点始终处于事务无法完成的中间状态。
丢失消息导致的数据不一致问题。在第二个阶段,如果发生局部网络问题,
3pc
引入超时机制。同时在协调者和参与者中都引入超时机制。
在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
1、协调者向参与者发送commit请求
2、协调者根据参与者的反应情况来决定,任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断
3、该阶段进行真正的事务提交
补偿事务(TCC)
TCC(Try-Confirm-Cancel)又称补偿事务。其核心思想是:"针对每个操作都要注册一个与其对应的确认和补偿(撤销操作)"。它分为三个操作:
Try阶段:主要是对业务系统做检测及资源预留。
Confirm阶段:确认执行业务操作。
Cancel阶段:取消执行业务操作。
TCC事务的处理流程与2PC两阶段提交类似,不过2PC通常都是在跨库的DB层面,而TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现。这种分布式事务的实现方式的优势在于,可以让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。
AQS
AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。
1.isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
2.tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
3.tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
4.tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
5.tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false
ReentrantLock:state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机