java面试问题

1. 聊聊你印象最深刻的项目,或者做了什么优化。

1.1 国投司库项目

1.1.1 报表优化

优化前:报表查询 30秒
优化后:建立物化视图,查询结果15秒

1.2 嘉程spring cloud 项目

1.2.1 商品缓存优化(Redis)

优化前:商品过期时间全部设置为8小时、热点数据设置一个月、数据的一致性
优化后:设置查询结果为空,在缓存中设置value值为-1;商品设置随机过期时间:1-30天不等(避免缓存雪崩);热点数据永不过期(避免缓存击穿),数据更新方式采用先写数据库再删缓存,在redis 与Mysql 之间加一个RocketMQ保证数据一致性。

public void updateDatabase(String key, Object value) {
    // 使用JDBC或ORM工具更新MySQL数据库
    String sql = "UPDATE your_table SET value = ? WHERE key = ?";
    // 使用PreparedStatement执行SQL
    // ...
}
public void updateRedis(String key, Object value) {
    // 使用Jedis或Lettuce客户端更新Redis
    redisTemplate.opsForValue().set(key, value);
}
public void sendMessage(String key, Object value) {
    // 使用RocketMQ的Producer发送消息
    producer.send(new Message("your_topic", key, JSON.toJSONString(value).getBytes()));
}

1.3 上海国药项目

1.3.1 单据优化

优化前:全量推送单据(每次变更单据,推送时,就会推送全部字段)
优化后:只推送变化的字段。

1.3.2 推送优化

优化前:只考虑null的情况

     String areaMdmId = Optional.ofNullable(supplierMain.getChild_mdm_id()).orElse(supplierMain.getId());

优化后:考虑空字符串的情况

    String areaMdmId = Optional.ofNullable(supplierMain.getChild_mdm_id())
            .filter(s -> !s.isEmpty()) // 过滤掉空字符串
            .orElseGet(() -> supplierMain.getId());

2. 你们接口幂等如何保证?

如果你调用下游接口口超时了,不考虑重试?如果重试,下游接口就需要支持幂等啦。
实现幂等一般有这 8 种方案:

  • select+insert+主键/唯一索引冲突
  • 直接 insert + 主键/唯一索引冲突
  • 状态机幂等
  • 抽取防重表
  • token 令牌
  • 悲观锁(如 select for update,很少用)
  • 乐观锁
  • 分布式锁

上海国药项目:单据用的是主键、一些参照用的是唯一索引(code作为唯一索引)直接 insert + 主键/唯一索引冲突
嘉程项目:直接 insert + 主键/唯一索引冲突

3. AQS原理

AQS(AbstractQueuedSynchronizer)的原理主要基于一个FIFO(先进先出)队列和一个同步状态(state)来实现线程的排队和同步。AQS的核心思想是,当线程尝试获取同步状态时,如果同步状态已经被其他线程占用,则将当前线程包装成一个节点(Node)并入队等待,否则直接获取同步状态。同时,AQS会确保只有队列中第一个节点的线程才能获取同步状态,其他节点需要等待前面的节点释放同步状态。

具体来说,AQS使用一个int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作,通过CAS(compare and swap,比较并交换)操作完成对State值的修改。当有线程请求锁时,AQS会将其封装成一个Node节点,并加入到等待队列中,线程则会进入阻塞状态。当持有锁的线程释放锁时,AQS会从等待队列中唤醒一个线程来获取锁,从而实现线程的同步和互斥。

AQS定义了两种资源共享方式:独占模式和共享模式。在独占模式下,只有一个线程能执行,如ReentrantLock。在共享模式下,多个线程可以同时获取资源,如Semaphore。AQS通过CLH(Craig, Landin, and Hagersten)队列的变体来实现这些模式,即将暂时获取不到锁的线程加入到队列中,通过内置的FIFO队列来完成资源的分配和回收。
具体详情请看:AQS原理

    AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。
    isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
    tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
    tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
    tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
    tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

    ReentrantLock是独占锁,所以实现了tryAcquire-tryRelease

    以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

 再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。
    一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。但AQS也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock。
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值