synchronized

qinyelin
发布于 2026-08-26 / 1 阅读
0
0

synchronized

synchronized 为什么能保证线程安全?

面试频率:★★★★★
工作频率:★★★★★


⚡30 秒速记(复习只看这里)

🟢 一句话

synchronized 是 Java 提供的同步锁机制,通过保证同一时间只有一个线程执行临界区代码,从而保证线程安全。


🟡 synchronized 三大作用

1. 原子性 ✅

多个操作:

只能一个线程执行

例如:

count++;

不会被其他线程打断。


2. 可见性 ✅

线程释放锁:

修改的数据刷新到主内存

其他线程获取锁:

读取最新数据

3. 有序性 ✅

禁止锁内代码发生不合理重排序。


🟠 synchronized 锁什么?

实例方法

public synchronized void test(){

}

锁:

当前对象 this

静态方法

public static synchronized void test(){

}

锁:

Class对象

例如:

UserService.class

代码块

synchronized(obj){

}

锁:

obj对象

🔴 一句话

synchronized 锁的是对象,不是方法。


📚 完整知识

一、为什么需要 synchronized?

看代码:

public class Counter {

    private int count = 0;


    public void add(){

        count++;

    }

}

两个线程:

线程A:

count++


线程B:

count++

看起来:

count = 2

但是实际:

读取 count

↓

增加1

↓

写回

不是一个操作。


可能:

线程A:

读取 count=0


线程B:

读取 count=0


线程A:

写入1


线程B:

写入1

最终:

count=1

发生:

数据丢失。


二、synchronized 如何解决?

加锁:

public synchronized void add(){

    count++;

}

执行:

线程A

↓

获取锁

↓

执行代码

↓

释放锁


线程B

↓

等待

↓

获取锁

↓

执行代码

最终:

count=2

三、什么是临界区?

临界区:

多线程同时访问可能产生问题的代码区域。

例如:

count++;

就是临界区。


synchronized:

作用:

保护临界区

保证:

同一时间只有一个线程进入。

四、synchronized 锁对象详解

1. 实例方法

代码:

public synchronized void save(){

}

等价于:

public void save(){

    synchronized(this){

    }

}

锁:

当前对象

例如:

UserService service = new UserService();

锁:

service对象

2. 静态方法

代码:

public static synchronized void save(){

}

锁:

UserService.class

因为:

静态方法属于类。


3. synchronized代码块

代码:

synchronized(lock){

}

锁:

lock对象

五、为什么不同对象不会互相阻塞?

例如:

对象A:

锁A

对象B:

锁B

两个线程:

线程1 → 对象A

线程2 → 对象B

可以同时执行。

因为:

锁不同。


六、synchronized 底层原理

Java对象:

都有:

对象头

其中包含:

Mark Word

里面记录:

锁状态

线程信息

加锁过程:

线程竞争

↓

修改对象头

↓

记录锁状态

↓

获得锁

七、synchronized 锁升级

JDK1.6 后:

synchronized 性能优化。

锁状态:

无锁

↓

偏向锁

↓

轻量级锁

↓

重量级锁

八、无锁

没有线程竞争。

例如:

synchronized(obj){

}

第一次进入。

直接执行。


九、偏向锁

场景:

只有一个线程访问。

例如:

线程A

↓

多次进入同一个锁

JVM:

记录:

哪个线程拥有锁

避免重复竞争。


十、轻量级锁

场景:

竞争不激烈。

多个线程尝试获取。

使用:

CAS

尝试获取锁。


十一、重量级锁

竞争严重。

线程无法获取锁。

进入:

阻塞等待

需要操作系统帮助调度。


十二、synchronized 和 volatile 区别

synchronizedvolatile
可见性
原子性
有序性
性能相对低
适合场景复杂同步状态通知

十三、为什么 volatile 不能替代 synchronized?

例如:

volatile int count;

count++;

volatile:

只能保证:

别人能看到最新值

但是:

count++:

包含:

读取

↓

计算

↓

写入

多个步骤。

仍然可能被打断。


synchronized:

保证:

整个过程不可被其他线程插入

十四、ConcurrentHashMap 为什么还需要 synchronized?

你之前学过:

ConcurrentHashMap:

CAS

+

synchronized

为什么不用 volatile?

因为:

volatile 只能保证:

变量可见

例如:

table[index]

引用变化。


但是修改链表:

Node.next修改

节点插入

红黑树调整

涉及多个操作。

需要:

synchronized

保证整体安全。


💼 工作场景

场景:订单数量统计

错误:

private int count;


public void add(){

    count++;

}

多个请求:

线程A

线程B

线程C

同时修改。

可能:

数据错误

正确:

public synchronized void add(){

    count++;

}

🎤 面试回答(30秒)

synchronized 是 Java 提供的同步机制,通过对象锁保证同一时间只有一个线程执行临界区代码,从而保证线程安全。它可以保证原子性、可见性和有序性。synchronized 锁的是对象,例如实例方法锁当前对象,静态方法锁 Class 对象,代码块锁指定对象。JDK1.6 后对 synchronized 做了优化,引入偏向锁、轻量级锁和重量级锁,提高性能。


🎯 面试追问

Q1:synchronized 锁的是方法吗?

答:

不是。

锁的是对象。


Q2:两个 synchronized 方法一定互斥吗?

答:

不一定。

如果锁的对象不同,不会互相阻塞。


Q3:volatile 能替代 synchronized 吗?

答:

不能。

volatile 不保证原子性。


Q4:ConcurrentHashMap 为什么不用一把 synchronized?

答:

锁粒度太大,影响并发性能。


Q5:synchronized 和 CAS 区别?

答:

CAS 是乐观锁,通过比较交换尝试修改。

synchronized 是悲观锁,没有获取锁的线程等待。


⚠️ 工作踩坑

1. synchronized 加在错误对象上

例如:

synchronized(new Object()){

}

每次都是新对象。

等于没有锁。


2. 认为 synchronized 方法锁整个类

错误:

实例方法锁对象。

静态方法锁 Class。


3. 认为 volatile 和 synchronized 一样

错误:

volatile 不保证原子性。


❓ 自测

  1. synchronized 为什么能保证线程安全?
  2. synchronized 锁的是什么?
  3. 实例方法和静态方法锁有什么区别?
  4. 为什么 count++ 需要锁?
  5. volatile 为什么不能替代 synchronized?
  6. synchronized 锁升级过程是什么?
  7. ConcurrentHashMap 为什么使用 synchronized?
  8. CAS 和 synchronized 有什么区别?
无锁

↓

偏向锁
(只有一个线程)


↓

轻量级锁
(少量竞争,CAS)


↓

重量级锁
(大量竞争,阻塞)

评论