synchronized锁的是对象的监视器(monitor),即对象本身;实例方法或synchronized(this)锁当前实例,静态方法锁Class对象,不同实例可并发执行。

Java里synchronized到底锁的是什么
它锁的是对象的监视器(monitor),不是代码块、不是方法、更不是变量。一个对象只有一个monitor,所以多个线程竞争同一对象的synchronized代码块时才会真正互斥。
常见误区:以为synchronized(this)和synchronized实例方法等价——确实等价;但synchronized静态方法锁的是Class对象,和实例锁完全不冲突。
- 实例方法或
synchronized(this)→ 锁当前实例(this) - 静态方法或
synchronized(ClassName.class)→ 锁该类的Class对象 - 不同实例的
synchronized方法可并发执行(锁对象不同) - 用字符串字面量作锁(如
synchronized("abc"))极危险:常量池共享,易导致意外交互
volatile能替代synchronized吗
不能。它只保证可见性和禁止指令重排序,不提供原子性。
典型反例:volatile int count; 然后做count++——这个操作包含读、加1、写三步,volatile无法保证这三步整体不可分割。结果就是并发自增大概率丢数据。
立即学习“Java免费学习笔记(深入)”;
- 适合场景:状态标志位(如
volatile boolean running = true;)、双重检查锁里的单例引用 - 不适合场景:复合操作(
++、--、+=)、依赖前值的判断(如if (flag) { doSomething(); flag = false; }) - 注意:volatile字段的写操作会刷新所有共享变量到主存,但不阻塞线程
ReentrantLock比synchronized多了什么
核心是“可编程性”:能响应中断、支持超时、可查询等待队列、支持公平/非公平策略、能绑定多个Condition。
但代价是必须显式lock()和unlock(),且unlock()必须放在finally块中,否则极易死锁。
ReentrantLock lock = new ReentrantLock();
try {
if (!lock.tryLock(1, TimeUnit.SECONDS)) {
throw new RuntimeException("acquire timeout");
}
// 临界区
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
-
lock.tryLock()可避免无限等待;tryLock(long, TimeUnit)支持超时 -
lock.lockInterruptibly()让等待线程可被interrupt()打断 - 默认是非公平锁(性能更好),构造时传
true可启用公平模式(按等待顺序分配) - 别在
synchronized里嵌套ReentrantLock,也没必要——两者解决同一类问题,混用只会增加复杂度
ConcurrentHashMap为什么不用全局锁
Java 8之后彻底弃用分段锁(Segment),改用CAS + synchronized锁单个Node桶(bucket)。
这意味着:读操作几乎无锁(依赖volatile和final语义),写操作只锁哈希冲突链表或红黑树的头节点,不同桶之间完全并发。
- 初始化时不立即分配数组,首次
put才触发initTable() -
size()返回的是估算值(多段计数累加),要精确值得用mappingCount() -
computeIfAbsent()是安全的,内部已处理并发重复计算问题;但不要在lambda里调用可能阻塞或递归的操作 - 遍历时若其他线程修改,不会抛
ConcurrentModificationException,但可能漏掉刚插入的元素(弱一致性)
ReentrantLock或误用volatile做计数,往往在线上压测时才暴露。









