在Java并发编程中,锁机制是最基础也是最重要的概念之一。根据不同的应用场景和性能需求,我们可以选择不同类型的锁来实现线程安全。
悲观锁的核心思想是"先加锁再操作",它假设并发冲突一定会发生。在Java中,synchronized关键字和ReentrantLock都是典型的悲观锁实现。
java复制// synchronized示例
public synchronized void transfer(Account from, Account to, int amount) {
if (from.getBalance() >= amount) {
from.debit(amount);
to.credit(amount);
}
}
// ReentrantLock示例
private final Lock lock = new ReentrantLock();
public void transfer(Account from, Account to, int amount) {
lock.lock();
try {
if (from.getBalance() >= amount) {
from.debit(amount);
to.credit(amount);
}
} finally {
lock.unlock();
}
}
关键细节与最佳实践:
乐观锁采取"先操作再验证"的策略,它假设并发冲突很少发生。Java中的原子类和数据库的version字段都是乐观锁的典型实现。
java复制// CAS操作示例
AtomicInteger counter = new AtomicInteger(0);
public void safeIncrement() {
int oldValue, newValue;
do {
oldValue = counter.get();
newValue = oldValue + 1;
} while (!counter.compareAndSet(oldValue, newValue));
}
// 数据库version字段示例
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 100 AND version = 5;
关键细节与最佳实践:
在分布式系统中,我们需要跨JVM的锁机制。常见的实现方式有基于Redis和ZooKeeper的方案。
Redis分布式锁实现要点:
java复制// 获取锁
SET lock_key unique_value NX PX 30000
// 释放锁
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
关键问题与解决方案:
ZooKeeper分布式锁实现要点:
死锁是多线程编程中最棘手的问题之一,理解其产生条件和预防方法至关重要。
资源有序分配法:
java复制// 定义资源类型顺序
public enum ResourceType {
PRINTER, SCANNER, PLOTTER;
}
// 按照固定顺序获取资源
public void process() {
Resource printer = getResource(ResourceType.PRINTER);
Resource scanner = getResource(ResourceType.SCANNER);
// 业务逻辑
release(scanner);
release(printer);
}
其他预防方法:
ConcurrentHashMap是Java并发包中最重要且最复杂的集合类之一,它的实现经历了多次优化。
JDK7中采用分段锁设计,将数据分为多个Segment,每个Segment独立加锁。
java复制// JDK7的put方法核心逻辑
public V put(K key, V value) {
Segment<K,V> s;
// 计算segment索引
int hash = hash(key);
int j = (hash >>> segmentShift) & segmentMask;
// 获取或创建segment
if ((s = (Segment<K,V>)UNSAFE.getObject(segments, j)) == null)
s = ensureSegment(j);
// 调用segment的put方法
return s.put(key, hash, value, false);
}
分段锁特点:
JDK8进行了重大改进,采用CAS+synchronized实现更细粒度的锁控制。
java复制// JDK8的putVal方法核心逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 计算hash
int hash = spread(key.hashCode());
// 循环直到插入成功
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 表为空则初始化
if (tab == null || (n = tab.length) == 0)
tab = initTable();
// 计算桶位置,如果为空则CAS插入
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))
break;
}
// 如果正在扩容则帮助扩容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
// 对桶加锁处理
synchronized (f) {
// 链表或红黑树插入逻辑
// ...
}
}
}
// 更新size计数
addCount(1L, binCount);
return null;
}
JDK8改进点:
线程池是Java并发编程的核心组件,合理使用线程池可以显著提升系统性能。
java复制ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 空闲线程存活时间
unit, // 时间单位
workQueue, // 工作队列
threadFactory, // 线程工厂
handler // 拒绝策略
);
七大核心参数:
流程图解:
code复制任务提交 → 核心线程是否满? → 否 → 创建核心线程执行
↓是
队列是否满? → 否 → 加入队列等待
↓是
线程数是否达最大? → 否 → 创建救急线程执行
↓是
执行拒绝策略
FixedThreadPool:
SingleThreadExecutor:
CachedThreadPool:
ScheduledThreadPool:
CPU密集型任务:
code复制线程数 = CPU核心数 + 1
理由:减少线程上下文切换,最大化CPU利用率
IO密集型任务:
code复制线程数 = CPU核心数 * (1 + 平均等待时间/平均计算时间)
经验值通常为CPU核心数的2-3倍
混合型任务:
可以将任务拆分为CPU密集和IO密集两部分,分别使用不同的线程池
动态调整策略:
ThreadLocal提供了线程局部变量,每个线程都有自己独立的变量副本。
java复制public class ThreadLocal<T> {
// 每个Thread对象内部都有一个ThreadLocalMap
static class ThreadLocalMap {
// Entry继承自WeakReference,key是弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// ... map实现
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = t.threadLocals;
if (map != null)
map.set(this, value);
else
t.threadLocals = new ThreadLocalMap(this, value);
}
}
关键点:
泄漏原因:
解决方案:
程序计数器:
Java虚拟机栈:
本地方法栈:
Java堆:
方法区(元空间):
新生代(Young Generation):
老年代(Old Generation):
对象分配与晋升规则:
引用计数法:
可达性分析算法:
标记-清除算法:
标记-整理算法:
复制算法:
分代收集算法:
Serial收集器:
ParNew收集器:
Parallel Scavenge收集器:
CMS收集器:
G1收集器:
ZGC收集器:
加载:
验证:
准备:
解析:
初始化:
启动类加载器(Bootstrap):
扩展类加载器(Extension):
应用程序类加载器(Application):
自定义类加载器:
工作流程:
优势:
破坏场景:
堆内存设置:
code复制-Xms512m // 初始堆大小
-Xmx2g // 最大堆大小
-Xmn256m // 新生代大小
-XX:NewRatio=2 // 老年代/新生代比例
-XX:SurvivorRatio=8 // Eden/Survivor比例
GC相关:
code复制-XX:+UseG1GC // 使用G1收集器
-XX:MaxGCPauseMillis=200 // 目标停顿时间
-XX:InitiatingHeapOccupancyPercent=45 // 触发并发标记的堆占用率
元空间:
code复制-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m
OOM处理:
code复制-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dump.hprof
命令行工具:
可视化工具:
CPU飙高排查:
内存泄漏排查:
死锁排查:
核心差异对比表:
| 特性 | InnoDB | MyISAM |
|---|---|---|
| 事务支持 | 支持ACID事务 | 不支持 |
| 锁机制 | 行级锁、表锁 | 仅表锁 |
| 外键支持 | 支持 | 不支持 |
| 崩溃恢复 | 支持 | 不支持 |
| 全文索引 | MySQL5.6+支持 | 支持 |
| 存储结构 | 聚簇索引 | 非聚簇索引 |
| 缓存 | 缓冲池(数据和索引) | 仅缓存索引 |
| 表空间 | 共享表空间/独立表空间 | 每个表三个文件(.frm,.MYD,.MYI) |
适用场景选择:
MEMORY:
ARCHIVE:
NDB:
原子性(Atomicity):
一致性(Consistency):
隔离性(Isolation):
持久性(Durability):
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现方式 |
|---|---|---|---|---|
| READ UNCOMMITTED | 可能 | 可能 | 可能 | 无锁 |
| READ COMMITTED | 不可能 | 可能 | 可能 | 快照读+行锁 |
| REPEATABLE READ | 不可能 | 不可能 | 可能(InnoDB不可能) | MVCC+间隙锁 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 | 全表锁 |
InnoDB的RR级别如何避免幻读:
多版本并发控制(Multi-Version Concurrency Control)是InnoDB实现非锁定读的关键。
核心组件:
隐藏字段:
undo log:
ReadView:
可见性判断规则:
B+树特点:
与B树对比优势:
InnoDB索引实现:
主键索引:
唯一索引:
普通索引:
联合索引:
覆盖索引:
创建原则:
SQL优化:
避免索引失效:
利用索引排序:
使用索引覆盖:
索引失效场景分析:
sql复制-- 索引失效示例
SELECT * FROM users WHERE LEFT(name,3) = 'abc'; -- 使用函数
SELECT * FROM users WHERE age+1 > 20; -- 使用运算
SELECT * FROM users WHERE phone = 13800138000; -- 类型转换
SELECT * FROM users WHERE name LIKE '%abc'; -- 前导通配符
按粒度分:
表锁:
行锁:
页锁:
按兼容性分:
共享锁(S锁):
排他锁(X锁):
InnoDB特殊锁:
意向锁:
间隙锁(Gap Lock):
临键锁(Next-Key Lock):
死锁产生条件:
InnoDB死锁检测:
避免死锁策略:
死锁排查:
sql复制-- 查看当前锁信息
SHOW ENGINE INNODB STATUS;
-- 查看锁等待
SELECT * FROM information_schema.INNODB_TRX;
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
关键字段解析:
JOIN优化:
子查询优化:
分页优化:
sql复制-- 低效写法
SELECT * FROM users ORDER BY id LIMIT 10000, 20;
-- 优化写法(利用索引覆盖)
SELECT * FROM users WHERE id >= (SELECT id FROM users ORDER BY id LIMIT 10000, 1) LIMIT 20;
COUNT优化:
命名规范:
字段设计:
表设计:
索引设计:
典型问题:
回答策略:
**