Java synchronized锁机制全解析,php 8.4.1 更新日志。
·
synchronized关键字的作用与原理
synchronized是Java中实现线程同步的关键字,主要用于解决多线程环境下的数据竞争问题。它能确保同一时刻只有一个线程可以访问被保护的代码块或方法。synchronized通过对象内部的一个称为"监视器锁"(monitor lock)的机制实现同步,每个Java对象都有一个与之关联的监视器。
synchronized的三种使用方式
- 实例方法同步 在方法声明中添加synchronized关键字,锁的是当前实例对象(this)。不同实例的对象锁互不干扰。
public synchronized void method() {
// 线程安全代码
}
- 静态方法同步 锁的是当前类的Class对象,属于类级别锁。所有实例共享同一把锁。
public static synchronized void staticMethod() {
// 线程安全代码
}
- 同步代码块 可以更细粒度地控制锁的范围和对象,提高并发性能。
public void method() {
synchronized(this) { // 可以指定任意对象作为锁
// 线程安全代码
}
}
synchronized的底层实现机制
JVM通过monitorenter和monitorexit两个字节码指令实现同步。当线程进入同步块时执行monitorenter获取锁,退出时执行monitorexit释放锁。每个对象都有一个关联的monitor,包含以下关键字段:
- _owner:当前持有锁的线程
- _recursions:重入次数计数器
- _EntryList:等待获取锁的线程队列
锁的升级过程
JDK6后对synchronized进行了优化,引入锁升级机制:
- 无锁状态:对象刚创建时的初始状态
- 偏向锁:适用于只有一个线程访问同步块的场景,通过CAS设置线程ID
- 轻量级锁:当有多个线程轻度竞争时,通过CAS自旋尝试获取锁
- 重量级锁:竞争激烈时升级为操作系统层面的互斥锁
使用注意事项
- 避免锁粒度过大:尽量缩小同步代码块范围,只保护必要的共享数据
- 防止死锁:避免嵌套获取多个锁,或按固定顺序获取锁
- 考虑性能影响:重量级锁会引发线程上下文切换,高并发场景考虑其他并发工具
- 锁对象选择:使用专门的对象作为锁而非业务对象,避免意外暴露
示例:线程安全的计数器实现
public class Counter {
private int count;
private final Object lock = new Object(); // 专用锁对象
public void increment() {
synchronized(lock) {
count++;
}
}
public int getCount() {
synchronized(lock) {
return count;
}
}
}
与其他同步机制的对比
- 与ReentrantLock比较:
- synchronized是JVM内置特性,ReentrantLock是API层面的实现
- ReentrantLock提供更灵活的锁获取方式(可中断、超时、公平锁等)
- synchronized在JDK6后性能差距已不明显
- 与volatile比较:
- volatile保证可见性和有序性,但不保证原子性
- synchronized保证原子性、可见性和有序性
最佳实践建议
- 优先使用同步代码块而非同步方法,缩小锁范围
- 对读写比例高的场景考虑读写锁(ReadWriteLock)
- 明确文档记录哪些对象被用作锁
- 在复杂同步需求下考虑java.util.concurrent包中的高级工具类
synchronized作为Java最基础的同步机制,理解其原理和使用方式是多线程编程的重要基础。随着JDK版本的演进,其性能已大幅优化,在大多数场景下仍然是简单可靠的选择。
更多推荐



所有评论(0)