蚂蚁金服-全球跨境支付-Java技术一面review

580 阅读6分钟

蚂蚁金服-全球跨境支付-Java技术一面review


自我介绍:学校经历 + 公司经历(需要时间加长)


pop项目介绍
项目的难点:架构设计 表结构 模块划分(1. 基础模块:物料信息 档期信息 印厂信息 2. 业务模块:设计 审核) 
 	  流程梳理(1. 需求来源多:日配非日配  2. 角色较多:md中台设计部法务品管公关印厂门店运营)
设计:pop系统作为需求的收集方,对外提供dubbo 
后续优化:
1. 复用原有业务流(蜂利器工作流),把前pop系统作为流程节点处理的操作系统 
2. 做一些业务开关,不同的需求有各自的配置,灵活性(成本高)


多线程安全的三大特性:原子性、可见性和有序性
Object类中的方法:wait,notify,notifyAll  必须在synchronized(obj){……}中调用 
               wait后重新获得锁才能继续执行 同步代码块结束后才会释放锁 等待唤醒机制;
volatile:轻量级锁  原理:主内存 本地内存
         每个线程执行时,先把变量从内存读到线程自己的本地内存中对其操作,操作完成后在某个时间刷新主内存。
         加上volatile修饰之后,将缓存的数据写会主内存;使得其它线程缓存失效。
         底层实现:赋值时加lock指令 会锁住总线 阻塞其它线程获取变量 且使得其它线程缓存失效!
         为什么每个线程维护一个变量的缓存:解决CPU运算速度与内存读写速度不匹配。
         
sync:在编译的字节码中加入了monitorenter和monitorexit这两个指令。
      monitorenter指令会获取锁对象,如果获取到了锁对象,就将锁计数器加1,未获取到则会阻塞当前线程。
      monitorexit指令会释放锁对象,同时将锁计数器减1。
      等待的线程是非公平调度 
      流程:获取锁失败进入锁池队列 -> 锁被持有的线程释放,幸运的一个线程被唤醒 -> 被唤醒的线程获得处理器 -> 获得锁 -> 执行同步代码。
1.6优化 锁的力度升级? 
    偏向锁 -> 轻量级锁 -> 重量级锁
    偏向锁:优化了只有一个线程重复获取锁和释放锁的消耗;
    轻量级锁:多个线程发生竞争时,通过CAS自旋操作来获取锁,极短时间重复尝试获取锁;
    重量级锁:没办法了 直接阻塞
总结:volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,
     synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
     
Lock:比sync更灵活,sync的缺点:一个线程获取锁 另一个线程只能等待下去 
     tryLock尝试获取锁 成功/不成功做其它事情 
     Lock是可以被中断的
     ReentrantLock可重入读写锁实现了Lock接口,进入一个sync方法,再进一个 诶 就是皮~
     可重入如何实现呐?
     volatile int state 计数+1 再次进入时 这个线程与持有锁的线程是否是同一个线程

悲观锁:sync 直接给丫的锁住
乐观锁:CAS CompareAndSwap:区别于sync同步锁的一种乐观锁 -> 
       AtomicInteger用volatile保证int value可见性,JNI操作CPU的CAS指令
       (多处理器时给cmpxchg指令加上lock前缀,确保对内存的读-改-写操作原子执行) 
       -> 优:高效解决原子操作 缺:ABA问题,循环时间长开销大,只能保证一个共享变量的原子操作


幂等性接口设计:
1. 场景是这样:库存系统定时给POP推送蜂特卖品,系统对外暴露dubbo接口,参数包括幂等性ID,这个字段设置成唯一索引,
   接口实现为先查后插。情景举例:两个重复请求    —— B端的接口幂等设计
2. C端 高并发的场景设计 上述存在的问题:第二次请求会报唯一索引冲突
   设计:Redis锁setNx 第一个请求获取锁插入数据 结果放到缓存里 第二次请求时 获取不到锁 就循环重试看缓存中有无结果。


线程池:
优势 :降低资源消耗 提高响应速度 
    参数:核心线程数(常驻线程数)、最大线程数、存活时间、阻塞队列(基于数组,基于链表)和拒绝策略(默认抛异常) 
    execute/submit提交任务
    线程池的流程分析:
    未达到核心线程数创建线程 达到将任务加入等待队列 队列已满新建非核心线程执行任务 队列满且达到最大线程数抛出异常
    (看过源码 如何实现多线程复用的!poll从缓存中获取任务 无则返回null / take从缓存中获取任务 无则阻塞)


hashMap实现:链表+红黑树 6/10亿 存储获取原理 扩容机制(初始容量 加载因子 扩容增量)
    hash函数(hash&length-1的好处 巧妙) 扩容会导致死循环
    hashMap并发插入:1.扩容时rehash死循环 2.只插入成功一个元素
currentHashMap:分段锁,包含两个静态内部类MapEntry和Segment(继承了ReentrantLock),
    前者用来封装映射表的键值对,后者用来充当锁的角色。


mysql :基础架构(连接 分析 优化 执行 分为server层和存储引擎层)
    索引:为啥要用B+树 场景决定:等值查询 区间查询 插入 N≈1200 
        (主键索引中/聚簇索引:非叶子结点记录key值 叶子结点存储行记录 非聚簇索引叶子结点记录主键索引值)
    回表 覆盖索引 最左前缀匹配 索引下推 
    事务特性:原子性 一致性 隔离性 持久性  隔离级别(读未提交 读提交 可重复读 串行化)
    事务隔离的实现:MVCC 多版本并发控制 每次更新数据时会生成一个数据版本(事务ID 和回滚操作undo log)
    数据版本具体指什么? 事务ID+回滚日志undo
    可重复读隔离级别实现:MVCC+行锁 (查询:一致性读 和 更新:快照读)


面试官点评:
    优点:追求技术+ACM+写博客
    缺点:缺少对项目思考

个人总结:自己学了很多知识,却没有表达出来,也是因为自己边工作边面试的结果。
认清自己会的点 长处:数据结构和算法、多线程并发、MySQL、幂等性接口设计和开发流程理解
ps:最近每天白天项目发布 半夜自己准备面试 突然想到了自己刚毕业找工作的辛酸 哈哈哈哈哈
   高谦虚同学说:如果老王能进支付宝 就嫁给他 哈哈哈哈哈