本文目录导读:

- 使用
synchronized关键字(最常用) - 使用
ReentrantLock(更强大、更灵活) - 使用
volatile关键字(轻量级同步) - 使用
Atomic类(CAS 无锁同步) - 使用
CountDownLatch/CyclicBarrier(线程协作同步) - 使用
BlockingQueue(生产者-消费者同步) - 使用
Condition(与ReentrantLock配合) - 总结对比
在Java中,线程同步是为了解决多个线程同时访问共享数据时可能导致的数据不一致或脏读问题,下面从最常用到高级的几种方式,结合案例进行说明。
使用 synchronized 关键字(最常用)
synchronized 可以修饰方法或代码块,保证同一时刻只有一个线程执行该段代码。
1 同步方法
class BankAccount {
private int balance = 100;
// 同步方法
public synchronized void withdraw(int amount) {
if (balance >= amount) {
System.out.println(Thread.currentThread().getName() + " 取款 " + amount);
balance -= amount;
System.out.println("余额: " + balance);
} else {
System.out.println(Thread.currentThread().getName() + " 余额不足");
}
}
}
public class SynchronizedMethodDemo {
public static void main(String[] args) {
BankAccount account = new BankAccount();
Thread t1 = new Thread(() -> account.withdraw(80), "张三");
Thread t2 = new Thread(() -> account.withdraw(80), "李四");
t1.start();
t2.start();
}
}
特点:
- 锁对象:
this(当前实例) - 如果方法是静态的,锁对象是
类.class
2 同步代码块(更灵活,缩小锁范围)
class BankAccount {
private int balance = 100;
private final Object lock = new Object(); // 专用锁对象
public void withdraw(int amount) {
synchronized (lock) { // 只锁关键代码
if (balance >= amount) {
System.out.println(Thread.currentThread().getName() + " 取款 " + amount);
balance -= amount;
System.out.println("余额: " + balance);
} else {
System.out.println(Thread.currentThread().getName() + " 余额不足");
}
}
// 其他不需要同步的代码可以放在外面
System.out.println("取款操作完成");
}
}
优点:只保护需要同步的代码,提高性能。
使用 ReentrantLock(更强大、更灵活)
java.util.concurrent.locks.ReentrantLock 可以手动加锁、解锁,支持公平锁、可中断锁、超时等待等。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class BankAccount {
private int balance = 100;
private final Lock lock = new ReentrantLock(); // 默认非公平锁
public void withdraw(int amount) {
lock.lock(); // 加锁
try {
if (balance >= amount) {
System.out.println(Thread.currentThread().getName() + " 取款 " + amount);
balance -= amount;
System.out.println("余额: " + balance);
} else {
System.out.println(Thread.currentThread().getName() + " 余额不足");
}
} finally {
lock.unlock(); // 必须释放锁
}
}
}
特点:
- 需要手动
lock()和unlock() - 必须放在
finally块中保证释放 - 支持
tryLock()尝试获取锁,避免死锁
使用 volatile 关键字(轻量级同步)
volatile 保证变量的可见性(每次读都从主存读取)和禁止指令重排序,但不保证原子性。
适用场景:一个变量被多个线程共享,且只有一个线程写,其他线程读。
public class VolatileDemo {
private volatile boolean running = true; // 保证可见性
public void stop() {
running = false;
}
public void run() {
while (running) {
// 循环工作
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
VolatileDemo demo = new VolatileDemo();
new Thread(demo::run).start();
Thread.sleep(1000);
demo.stop(); // 主线程修改,子线程能立刻看到
}
}
注意:
volatile不能替代synchronized处理复合操作(如count++)。
使用 Atomic 类(CAS 无锁同步)
java.util.concurrent.atomic 包提供了基于 CAS(Compare-And-Swap) 的原子类,性能比 synchronized 高。
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicDemo {
private static AtomicInteger count = new AtomicInteger(0);
public static void increment() {
count.incrementAndGet(); // 原子自增
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终结果: " + count.get()); // 一定是2000
}
}
常用原子类:
AtomicInteger/AtomicLongAtomicBooleanAtomicReference<V>
使用 CountDownLatch / CyclicBarrier(线程协作同步)
这类工具用于多个线程之间互相等待。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); // 等待3个线程完成
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 执行完成");
latch.countDown(); // 计数器减1
}).start();
}
latch.await(); // 主线程等待所有子线程完成
System.out.println("所有线程执行完毕,主线程继续");
}
}
使用 BlockingQueue(生产者-消费者同步)
BlockingQueue 内部实现了线程安全,是经典的生产者-消费者模式解决方案。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
// 生产者
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
queue.put(i); // 如果队列满则阻塞
System.out.println("生产者生产: " + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 消费者
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
Integer val = queue.take(); // 如果队列空则阻塞
System.out.println("消费者消费: " + val);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
使用 Condition(与 ReentrantLock 配合)
Condition 提供了类似 wait/notify 的功能,但更灵活(可多条件,可超时)。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean ready = false;
public void await() throws InterruptedException {
lock.lock();
try {
while (!ready) {
condition.await(); // 等待
}
System.out.println("被唤醒");
} finally {
lock.unlock();
}
}
public void signal() {
lock.lock();
try {
ready = true;
condition.signal(); // 唤醒一个等待线程
} finally {
lock.unlock();
}
}
}
总结对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
synchronized |
简单、自动释放锁 | 不够灵活、不能中断 | 大多数同步场景 |
ReentrantLock |
灵活、可中断、公平锁 | 需要手动释放锁 | 高性能/复杂同步需求 |
volatile |
轻量、保证可见性 | 不保证原子性 | 单一写多读变量 |
Atomic* |
高性能无锁 | 只支持单变量 | 计数器、标志位 |
CountDownLatch |
线程间协调 | 一次性的 | 等待多个线程完成 |
BlockingQueue |
线程安全、解耦 | 需要额外队列 | 生产者-消费者 |
Condition |
多条件等待 | 复杂 | 精细的等待/通知 |
建议:
- 简单场景用
synchronized - 高性能/灵活场景用
ReentrantLock或Atomic - 线程协调用
CountDownLatch/CyclicBarrier/BlockingQueue