✅ 1. 线程同步概述
在 多线程环境 下,如果多个线程同时访问 共享资源,可能会导致 数据不一致 或 线程安全问题。为了避免这种情况,我们需要 线程同步机制 来 控制多个线程对共享资源的访问。
常见的线程同步方式:
- 同步代码块(synchronized 代码块)
- 同步方法(synchronized 方法)
- 显示锁(Lock 机制,如 ReentrantLock)
✅ 2. 同步代码块(synchronized 代码块)
🔹 概念
- 通过
synchronized
关键字 锁定某个对象,保证 同一时刻只有一个线程 执行同步代码块。
- 适用于部分代码需要同步 的情况,提高性能。
🔹 代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class SharedResource { private int count = 0;
public void increment() { synchronized (this) { count++; System.out.println(Thread.currentThread().getName() + " -> " + count); } } }
public class SynchronizedBlockExample { public static void main(String[] args) { SharedResource resource = new SharedResource();
Runnable task = () -> { for (int i = 0; i < 5; i++) { resource.increment(); } };
Thread t1 = new Thread(task, "线程1"); Thread t2 = new Thread(task, "线程2");
t1.start(); t2.start(); } }
|
🔹 解析
synchronized (this)
锁定当前实例,多个线程访问 increment()
方法时 会串行执行,避免竞态条件。
- 适用于部分代码需要同步 的场景,而不是锁住整个方法,能提高程序 并发性能。
✅ 3. 同步方法(synchronized 方法)
🔹 概念
- 在方法前加
synchronized
,使得 整个方法 在同一时刻只能被 一个线程访问。
- 适用于 整个方法都需要同步 的场景,但可能会 影响并发性能。
🔹 代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class SharedResource { private int count = 0;
public synchronized void increment() { count++; System.out.println(Thread.currentThread().getName() + " -> " + count); } }
public class SynchronizedMethodExample { public static void main(String[] args) { SharedResource resource = new SharedResource();
Runnable task = () -> { for (int i = 0; i < 5; i++) { resource.increment(); } };
Thread t1 = new Thread(task, "线程1"); Thread t2 = new Thread(task, "线程2");
t1.start(); t2.start(); } }
|
🔹 解析
synchronized
修饰方法,相当于 synchronized (this) {}
。
- 适用于整个方法都需要同步 的情况,但会影响 程序并发性能。
⚠️ 静态方法同步
synchronized static
方法锁定的是 类对象(Class 对象),而不是实例:
1 2 3 4 5 6 7 8
| class SharedResource { private static int count = 0;
public static synchronized void increment() { count++; System.out.println(Thread.currentThread().getName() + " -> " + count); } }
|
等价于:
1 2 3 4 5
| public static void increment() { synchronized (SharedResource.class) { count++; } }
|
适用于 需要对 类级别共享资源 进行同步的场景。
✅ 4. Lock(显式锁机制)
🔹 概念
Lock
(如 ReentrantLock
)提供 更灵活的同步控制,相比 synchronized
具有以下优势:
- 支持公平锁和非公平锁(
synchronized
只能是非公平锁)
- 可以尝试获取锁(避免死锁)
- 可以中断(线程可被打断)
🔹 代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
class SharedResource { private int count = 0; private final Lock lock = new ReentrantLock();
public void increment() { lock.lock(); try { count++; System.out.println(Thread.currentThread().getName() + " -> " + count); } finally { lock.unlock(); } } }
public class LockExample { public static void main(String[] args) { SharedResource resource = new SharedResource();
Runnable task = () -> { for (int i = 0; i < 5; i++) { resource.increment(); } };
Thread t1 = new Thread(task, "线程1"); Thread t2 = new Thread(task, "线程2");
t1.start(); t2.start(); } }
|
🔹 解析
lock.lock()
获取锁,lock.unlock()
释放锁,确保 finally
代码块中释放锁,防止 死锁。
ReentrantLock
是 可重入锁,同一线程可多次获取锁。
✅ 5. synchronized
vs Lock
对比
对比项 |
synchronized |
Lock(ReentrantLock) |
锁的类型 |
隐式锁(JVM 负责加锁解锁) |
显式锁(手动加锁解锁) |
公平锁 |
不支持 |
支持 公平锁/非公平锁 |
可中断性 |
不可中断,必须执行完 |
可中断(lockInterruptibly() ) |
尝试获取锁 |
不支持 |
支持 tryLock() (避免死锁) |
性能 |
JVM 优化,性能较高 |
稍低,但更灵活 |
✅ 使用建议
- 简单的同步(如线程安全的计数器):✅
synchronized
- 需要公平锁、可中断锁 或 避免死锁:✅
ReentrantLock
- 涉及多个共享资源(如数据库事务、读写锁):✅
ReentrantLock
✅ 6. 总结
同步方式 |
特点 |
同步代码块 (synchronized (this) {} ) |
适用于 部分代码 需要同步,提高性能 |
同步方法 (synchronized 方法名() {} ) |
适用于 整个方法 需要同步,简单但影响并发 |
Lock(ReentrantLock) |
显式锁,支持 公平锁、可中断、尝试获取锁,适用于 高级同步 |
推荐方式:
- 首选
synchronized
(简单、高效)
- 如果需要复杂控制(如公平锁、可中断),使用
Lock