Java线程同步
Java线程同步[编辑 | 编辑源代码]
Java线程同步是多线程编程中的核心概念,用于控制多个线程对共享资源的访问,防止出现数据不一致或竞态条件(Race Condition)等问题。当多个线程同时访问和修改同一数据时,如果没有适当的同步机制,可能会导致不可预测的结果。Java提供了多种同步机制来确保线程安全。
为什么需要线程同步?[编辑 | 编辑源代码]
在多线程环境中,多个线程可能同时访问和修改共享数据。例如,两个线程同时对一个银行账户进行存款和取款操作,如果没有同步机制,可能会导致账户余额计算错误。线程同步的主要目的是:
- 保证数据一致性:确保共享数据在任何时候都处于一致状态。
- 避免竞态条件:防止多个线程同时修改数据导致逻辑错误。
- 实现线程协作:协调多个线程的执行顺序。
Java中的同步机制[编辑 | 编辑源代码]
Java提供了多种同步机制,主要包括:
1. synchronized 关键字[编辑 | 编辑源代码]
`synchronized` 是Java中最基本的同步机制,可以用于方法或代码块,确保同一时间只有一个线程可以执行被同步的代码。
同步方法[编辑 | 编辑源代码]
public class Counter {
private int count = 0;
// 同步方法
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
同步代码块[编辑 | 编辑源代码]
public class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
// 同步代码块
synchronized (lock) {
count++;
}
}
}
2. Lock 接口[编辑 | 编辑源代码]
Java 5引入了`java.util.concurrent.locks.Lock`接口,提供了比`synchronized`更灵活的锁机制,如`ReentrantLock`。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
3. volatile 关键字[编辑 | 编辑源代码]
`volatile` 用于确保变量的可见性,即当一个线程修改了`volatile`变量时,其他线程能立即看到最新的值。
public class SharedData {
private volatile boolean flag = false;
public void setFlag(boolean value) {
flag = value;
}
public boolean getFlag() {
return flag;
}
}
4. 原子类(Atomic Classes)[编辑 | 编辑源代码]
Java提供了`java.util.concurrent.atomic`包中的原子类(如`AtomicInteger`、`AtomicLong`等),它们使用CAS(Compare-And-Swap)机制实现无锁线程安全操作。
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
实际应用案例[编辑 | 编辑源代码]
银行账户转账问题[编辑 | 编辑源代码]
假设有两个线程同时对一个银行账户进行存款和取款操作,如果不使用同步机制,可能会导致余额计算错误。
public class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
balance = initialBalance;
}
// 同步方法确保存款操作线程安全
public synchronized void deposit(double amount) {
balance += amount;
}
// 同步方法确保取款操作线程安全
public synchronized void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
} else {
System.out.println("余额不足");
}
}
public synchronized double getBalance() {
return balance;
}
}
生产者-消费者问题[编辑 | 编辑源代码]
生产者线程生成数据,消费者线程消费数据,使用`wait()`和`notify()`实现线程协作。
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumer {
private final Queue<Integer> queue = new LinkedList<>();
private final int CAPACITY = 5;
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (this) {
while (queue.size() == CAPACITY) {
wait(); // 队列满时等待
}
System.out.println("生产者生产: " + value);
queue.add(value++);
notify(); // 唤醒消费者
Thread.sleep(1000);
}
}
}
public void consume() throws InterruptedException {
while (true) {
synchronized (this) {
while (queue.isEmpty()) {
wait(); // 队列空时等待
}
int value = queue.poll();
System.out.println("消费者消费: " + value);
notify(); // 唤醒生产者
Thread.sleep(1000);
}
}
}
}
线程同步的常见问题[编辑 | 编辑源代码]
死锁(Deadlock)[编辑 | 编辑源代码]
当两个或多个线程互相持有对方需要的资源时,可能会导致死锁。例如:
活锁(Livelock)[编辑 | 编辑源代码]
线程不断重试某个操作,但始终无法取得进展。例如两个线程互相让出资源,导致都无法执行。
饥饿(Starvation)[编辑 | 编辑源代码]
某些线程长时间得不到执行机会,通常是由于优先级设置不合理或锁竞争不公平导致。
总结[编辑 | 编辑源代码]
Java线程同步是多线程编程的核心,合理使用`synchronized`、`Lock`、`volatile`和原子类可以确保线程安全。同时,需要注意避免死锁、活锁和饥饿等问题。在实际开发中,应根据具体场景选择合适的同步机制。