2024年10月19日 虾皮购物 一面 已挂
以下是整理后的 Markdown 格式内容,优化了标题层级、列表嵌套和代码块显示,提升可读性:
1. Java中的锁机制 - 可重入锁
锁机制
- 同步方法和同步块
- Lock接口
可重入锁(Reentrant Lock)
定义:允许同一线程多次获取同一把锁,避免递归调用或多层方法调用时的死锁问题。
特点:
- 重入性
- 公平性策略
- 手动锁管理
示例代码:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void methodA() {
lock.lock(); // 获取锁
try {
System.out.println("In method A");
methodB(); // 同一线程可重入调用
} finally {
lock.unlock(); // 释放锁
}
}
public void methodB() {
lock.lock(); // 再次获取锁
try {
System.out.println("In method B");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
new ReentrantLockExample().methodA();
}
}
总结:可重入锁通过计数器实现重入逻辑,适用于递归调用、复杂同步场景,需注意手动释放锁以避免资源泄漏。
2. 可重入锁的使用场景
- 递归调用
import java.util.concurrent.locks.ReentrantLock;
public class RecursiveLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int counter = 0;
public void increment() {
lock.lock();
try {
counter++;
System.out.println("Counter: " + counter);
if (counter < 5) increment(); // 递归调用
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
new RecursiveLockExample().increment();
}
}
- 多线程资源共享
import java.util.concurrent.locks.ReentrantLock;
public class SharedResource {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try { count++; }
finally { lock.unlock(); }
}
// 省略getCount和多线程测试代码
}
- 复杂对象状态管理
public class ComplexObject {
private final ReentrantLock lock = new ReentrantLock();
private String state = "";
public void updateState(String newState) {
lock.lock();
try {
state = newState;
processState(); // 同一线程内调用其他方法
} finally { lock.unlock(); }
}
private void processState() { /* 处理状态 */ }
}
- 保证顺序执行
public class SequentialExecution {
private final ReentrantLock lock = new ReentrantLock();
private boolean initialized = false;
public void performTask() {
lock.lock();
try {
if (!initialized) initialize(); // 确保先初始化
// 执行任务
} finally { lock.unlock(); }
}
}
总结:可重入锁在递归、资源竞争、状态管理和顺序控制场景中能有效避免死锁,提升并发安全性。
3. 讲一下AQS(AbstractQueuedSynchronizer)
AQS的基本概念
- 核心结构:
AQS的主要方法
方法名 | 作用描述 |
| 尝试获取独占锁,成功返回 |
| 尝试释放独占锁,成功返回 |
| 阻塞获取锁,调用 |
| 释放锁,调用 |
| 获取/设置同步状态 |
AQS的实现示例(独占锁)
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class MyLock {
private static class Sync extends AbstractQueuedSynchronizer {
// 尝试获取锁(CAS操作)
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) { // 状态0→1表示获取锁
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 尝试释放锁
protected boolean tryRelease(int arg) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 判断当前线程是否持有锁
protected boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
}
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
}
使用AQS的场景
- JUC组件底层实现:
AQS的优势
- 高性能:通过CAS和队列减少线程阻塞,适用于高并发场景。
- 灵活性:支持自定义同步逻辑(独占/共享模式)。
- 可扩展:通过继承AQS并实现抽象方法,快速实现自定义同步器。
总结:AQS是Java并发包的核心框架,通过队列和状态管理实现高效同步,是理解ReentrantLock等工具的基础。
4. Redis的相关数据结构
1. 字符串(String)
- 底层结构:
2. 哈希(Hash)
- 底层结构:
3. 列表(List)
- 底层结构:
4. 集合(Set)
- 底层结构:
5. 有序集合(Sorted Set)
- 底层结构:
5. 为什么每种数据类型一般有两种数据结构?
设计思想:空间与时间的权衡,根据数据量动态切换结构以优化性能。
- 小数据量场景:使用紧凑结构(如压缩列表),减少内存占用。
- 大数据量场景:切换为普通结构(如哈希表、跳表),提升操作效率。
优势:在内存占用和操作性能间取得平衡,适应不同业务场景。
6. JVM相关内存结构
1. 方法区(Method Area)
- 作用:存储类元数据(类名、字段、方法、常量池、静态变量等)。
- 演进:
2. 堆(Heap)
- 作用:存储对象实例,是GC的主要管理区域。
- 分区:
3. 栈(Java Virtual Machine Stack)
- 作用:每个线程独有,存储局部变量、方法调用栈帧(包括操作数栈、动态链接、返回地址等)。
- 特点:线程私有,后进先出(LIFO),栈深度过大会导致
StackOverflowError。
4. 程序计数器(Program Counter Register)
- 作用:记录当前线程执行的字节码指令地址,线程切换时用于恢复执行位置。
- 特点:线程私有,是JVM中唯一无内存溢出风险的区域。
5. 本地方法栈(Native Method Stack)
- 作用:存储本地方法(
native修饰的方法)的调用栈帧,如调用C/C++代码时使用。
7. HashMap的底层原理
1. 数据结构
- 数组+链表/红黑树:
2. 哈希函数
- 扰动处理:对
hashCode()返回值进行高位移异或((h = key.hashCode()) ^ (h >>> 16)),降低哈希冲突概率。
3. 添加元素流程
- 计算键的哈希值,确定数组索引。
- 若桶为空,直接插入新节点。
- 若桶非空:
- 若链表长度≥8且数组容量≥64,链表转红黑树。
- 若元素个数超过
threshold(容量×负载因子,默认16×0.75=12),触发扩容。
4. 获取元素流程
- 计算键的哈希值,定位数组索引。
- 若桶为空,返回
null; - 遍历链表/红黑树,匹配键相等的节点,返回对应值。
5. 扩容机制
- 触发条件:元素个数 >
threshold。 - 扩容逻辑:
6. 线程安全
- 非线程安全:多线程并发修改可能导致链表成环、数据丢失等问题。
- 解决方案:
总结:HashMap通过哈希函数和动态数组实现高效存取,通过链表/红黑树处理冲突,适用于单线程高性能场景,多线程需额外同步机制。
#面试问题记录##牛客创作赏金赛#27双非 Java后端开发面经 文章被收录于专栏
来源于自己的记录但是并不都是自己的面试经历 所有解答均是主观看法一个字一个字的敲的...

传音控股公司福利 317人发布