Java 线程安全锁是保证线程在并发环境下安全访问共享资源的关键机制。理解其原理并能在实际项目中正确使用,对于编写高效、稳定的并发程序至关重要。本文将深入探讨 Java 线程安全锁的原理,并结合实战案例进行分析。
一、线程安全锁原理
线程安全锁主要基于两个核心概念:互斥和条件变量。
1. 互斥
互斥锁(Mutex Lock)确保同一时间只有一个线程可以访问共享资源。Java 中常用的互斥锁有 synchronized 关键字和 ReentrantLock 类。
a. synchronized 关键字
synchronized 关键字可以用于方法或代码块,实现互斥锁的功能。当一个线程进入同步代码块时,它会获取锁,其他线程则被阻塞,直到锁被释放。
public synchronized void synchronizedMethod() {
// 代码块
}
b. ReentrantLock 类
ReentrantLock 类提供了比 synchronized 更灵活的锁操作。它可以显式地获取和释放锁,支持公平锁和非公平锁。
ReentrantLock lock = new ReentrantLock(true); // 创建公平锁
lock.lock(); // 获取锁
try {
// 代码块
} finally {
lock.unlock(); // 释放锁
}
2. 条件变量
条件变量允许线程在某些特定条件下等待,并在条件满足时被唤醒。Java 中常用的条件变量有 Object.wait()、Object.notify() 和 Object.notifyAll() 方法。
public class ConditionExample {
public synchronized void conditionMethod() {
while (someCondition) {
wait();
}
// 条件满足后的代码块
}
}
二、实战案例分析
下面通过一个简单的银行账户案例,展示如何使用线程安全锁来保证账户操作的线程安全。
1. 银行账户类
public class BankAccount {
private int balance;
public BankAccount(int initialBalance) {
this.balance = initialBalance;
}
public synchronized void deposit(int amount) {
balance += amount;
}
public synchronized void withdraw(int amount) {
if (amount <= balance) {
balance -= amount;
} else {
throw new IllegalArgumentException("Insufficient funds");
}
}
public synchronized int getBalance() {
return balance;
}
}
2. 多线程并发操作
public class BankAccountTest {
public static void main(String[] args) {
BankAccount account = new BankAccount(100);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
account.deposit(10);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
account.withdraw(10);
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final balance: " + account.getBalance());
}
}
在这个案例中,我们创建了两个线程,一个用于存款,另一个用于取款。通过使用 synchronized 关键字,我们保证了每次只有一个线程可以执行存款或取款操作,从而保证了账户余额的正确性。
三、总结
线程安全锁是 Java 并发编程中不可或缺的一部分。通过理解线程安全锁的原理,并结合实际案例进行分析,我们可以更好地掌握如何编写线程安全的代码。在实际应用中,合理选择和使用线程安全锁,能够有效避免并发问题,提高程序的稳定性和性能。
