9.9日面内容:
java常用集合是什么?
单列集合和双列集合,不介绍了。
用过什么并发集合?并介绍一下你的实际如何用的
介绍的是countDownLatch的使用用法。面试官重点给我说了需要设置主线程需要超时释放,带入参数。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CountDownLatchTimeoutExample {
public static void main(String[] args) throws InterruptedException {
// 1. 创建 CountDownLatch,计数器初始值为 3(表示需要等待 3 个任务完成)
CountDownLatch latch = new CountDownLatch(3);
// 2. 启动 3 个异步任务
for (int i = 1; i <= 3; i++) {
final int taskId = i;
new Thread(() -> {
try {
System.out.println("任务 " + taskId + " 开始执行...");
// 模拟任务执行时间(任务1: 2秒,任务2: 4秒,任务3: 6秒)
long sleepTime = (taskId == 3) ? 6000 : taskId * 2000; // 任务3故意超时(6秒)
Thread.sleep(sleepTime);
System.out.println("任务 " + taskId + " 执行完成!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("任务 " + taskId + " 被中断!");
} finally {
// 3. 任务完成后,计数器减1
latch.countDown();
}
}).start();
}
// 4. 主线程等待:最多等待5秒,超时后不再等待
System.out.println("主线程等待所有任务完成(最多等5秒)...");
long startTime = System.currentTimeMillis();
boolean allCompleted = latch.await(5, TimeUnit.SECONDS); // 关键:超时释放
long endTime = System.currentTimeMillis();
// 5. 根据等待结果处理后续逻辑
if (allCompleted) {
System.out.println("所有任务在" + (endTime - startTime) + "毫秒内完成!");
} else {
System.out.println("等待超时!剩余未完成任务数:" + latch.getCount());
}
// 主线程继续执行后续操作(无论任务是否全部完成)
System.out.println("主线程继续执行后续逻辑...");
}
}
mysql的锁有哪些?
全局锁:flush tables with read lock 将所有的表都锁住,只能读取不能写入。
表级锁:lock tables table_name read/wirte 锁定某张表。
意向锁:意向写锁和意向读锁(加行级锁自动加上对应的写/读锁,默认的行级锁是临键锁)。
行级锁:记录锁,间隙锁,临键锁。
mysql事务四大特性,然后如何实现的
原子性:通过undolog+redolog来实现的。要么全做要么要不做。
一致性:主键约束和外键约束。
隔离性:读的话是通过多版本并发控制。写的话是锁机制。
mysql的持久性:
通过redolog和wal技术。
描述b+树
b+树的非叶子结点是只存放索引,而叶子结点可能存放的是数据记录,也可能存放的是指向数据记录的指针,并且叶子结点是通过了双向链表组合起来的。
索引失效的场景 重点问了 联合索引 举了几个例子问我是否满足
1.模糊查询like,当百分号在右边边的时候不会失效,就晋升为了前缀索引了,只要百分号在前面就会失效。
2.查询列当中使用了函数。
3.查询列当中使用了条件表达式。
4.索引列发生了隐式数据类型转换也会失效。
5.不满足最左匹配原则也会失效。范围查询也算是走了索引。
6.where条件on前后必须都满足索引的需求,否则会走全盘扫描。
redis 基本数据类型
string,set,zset,list,hash
接口慢如何排查,如何解决?
1.使用explain分析是否走索引
2.加redis缓存
3.加索引
4.走联合索引
5.对于大的string类型使用前缀索引
6.尽量走索引覆盖
超卖问题,乐观锁
超卖问题发生的原因是高并发下可能同时有多个线程对某一个共享变量同时修改值,而共享变量的修改分为了取出,判断,减1,三个子步骤,如果说线程交替执行的话,可能会导致库存为负的情况。
使用CAS的修改库存,修改前读取出一个预期值,然后要修改的时候用预期值和内存当中的进行比较看是否相同,如果说不同就不能修改。
线程如何启动的方式,线程池的拒绝策略和任务队列?
1.继承Thread类并重写run方法
2.实现runnable接口,并重写run方法,并把它做为参数传递给Thread类
3.实现callable接口,重写call方法,可以有返回值(通过futureTask.get获取),包装进一个futrueTask,然后将futureTask传递给Thread类。
4.使用线程池
拒绝策略:
1.CallerRunsPolices:使用线程池调用者的线程去完成这个任务。
2.AbortPolicy:直接抛出异常。
3.DiscardPolicy:直接丢弃不做任何处理,
4.DiscardOldestPolicy:丢弃最老的任务。
任务队列:
1. ArrayBlockingQueue:有界数组队列(固定容量):适合流量稳定。
2. SynchronousQueue:同步队列(无缓冲,直接传递)
3.LinkedBlockingQueue:无界/有界链表队列(默认无界)
4.PriorityBlockingQueue:优先级队列(无界)
如何设置核心线程数?
核心线程数的设置规则:
I/O密集的话核心线程数=CPU核数*2
CPU密集的话 核心线程数=CPU+1
分场景看:
电商场景,高并发的场景,线程数多,并且处理速度快,此时的核心线程数就需要乘以2并且使用同步队列(不缓存),拒绝策略直接选择抛出异常。
后台数据系统,核心线程数和最大线程数都设置为cpu数,使用有界队列,拒绝策略由调用者去执行线程。
你的项目当中的redis是怎么使用的?如何保证数据的一致性?
1.在质量模块当中有一个业务是审批计划,计划分为了计划详细表,计划详细表里面存储的是不同类型的任务需要在,一个计划可以对应多个计划明细结点,当计划被审批通过的时候,就会根据计划明细表生成多个任务,主键是planNo的数据,通过业务的前缀加上计划的主键做为lock的key。
2.完成授权申请这个业务的时候由于审批的结点很多,所以说会产生读多写少的场景,而一条授权申请的表的字段又很多,查询数据库的效率也低,所以说就将授权申请的数据缓存到了数据库当中。采用的是旁路缓存模式,读的话就是如果说缓存当中读取到了这个数据就直接返回没有读的话就就写到缓存当中并设置过期时间,写的话就是写数据库再删缓存,在一定程度上保障了MySQL和redis的一致性。并且在改的时候加上了分布式锁,防止多个线程去修改同一个流程,在数据库加上了一个审批人字段,当去审批的时候就会先去用redisson锁去竞争修改审批人,如果说竞争成功了就将修改人改为自己的userid,而去修改这个审批人id的时候又使用到了CAS去更改,否则就显示被别人审批了,后面只有去查询到当前登录用户和数据库的审批用户才能修改。