1. 线程同步概述

多线程环境 下,如果多个线程同时访问 共享资源,可能会导致 数据不一致线程安全问题。为了避免这种情况,我们需要 线程同步机制控制多个线程对共享资源的访问

常见的线程同步方式

  1. 同步代码块(synchronized 代码块)
  2. 同步方法(synchronized 方法)
  3. 显示锁(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) { // 只锁定 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