性能差异
- 原子操作:
- 原理:原子操作类(如
AtomicInteger
、AtomicLong
等)利用硬件级别的原子指令(如CAS
- Compare - And - Swap)来实现操作的原子性。在没有竞争或者竞争较小的情况下,不需要线程挂起和恢复等上下文切换开销。
- 性能优势:在低竞争环境下,性能表现优异,因为它不需要像锁那样进行线程的阻塞和唤醒操作,减少了线程上下文切换的开销。例如,在一个计数器场景中,多个线程对计数器进行自增操作,如果竞争不激烈,使用
AtomicInteger
的性能会比使用锁更高。
- 锁机制:
- 原理:无论是
synchronized
关键字还是ReentrantLock
,本质上都是通过锁定一个对象或者资源,使得同一时间只有一个线程能够访问被保护的代码块或者资源。当一个线程获取锁失败时,会被阻塞,进入等待队列,直到锁被释放。
- 性能劣势:在高竞争环境下,频繁的线程阻塞和唤醒会导致大量的上下文切换开销,降低系统性能。例如,多个线程频繁竞争一个锁来访问临界区代码,会使得线程在等待锁和执行代码之间频繁切换,降低整体的吞吐量。
应用场景
- 原子操作适用场景:
- 计数器场景:例如统计网站的访问量,多个线程并发进行计数操作。使用
AtomicInteger
可以简单高效地实现,示例代码如下:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
- 状态标记场景:比如标记任务是否完成,使用
AtomicBoolean
,代码示例:
import java.util.concurrent.atomic.AtomicBoolean;
public class TaskMarker {
private AtomicBoolean taskFinished = new AtomicBoolean(false);
public void setTaskFinished() {
taskFinished.set(true);
}
public boolean isTaskFinished() {
return taskFinished.get();
}
}
- 锁机制适用场景:
- 复杂同步逻辑场景:当需要保证多个操作的原子性,并且操作之间存在依赖关系时,锁机制更合适。例如银行转账操作,从一个账户扣款并向另一个账户加款,这两个操作需要在同一锁的保护下,确保数据一致性。示例代码(使用
synchronized
):
public class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
public synchronized void transfer(BankAccount to, double amount) {
if (this.balance >= amount) {
this.balance -= amount;
to.balance += amount;
}
}
public double getBalance() {
return balance;
}
}
- 资源共享场景:当多个线程需要访问和修改共享资源,并且需要对资源进行复杂的读写控制时,如数据库连接池的管理,使用
ReentrantLock
可以提供更灵活的锁控制(如可重入、公平性等)。示例代码:
import java.util.concurrent.locks.ReentrantLock;
public class ConnectionPool {
private ReentrantLock lock = new ReentrantLock();
// 模拟连接池
private int availableConnections;
public ConnectionPool(int initialConnections) {
this.availableConnections = initialConnections;
}
public void getConnection() {
lock.lock();
try {
if (availableConnections > 0) {
availableConnections--;
// 这里可以返回实际的连接对象
} else {
// 处理没有可用连接的情况
}
} finally {
lock.unlock();
}
}
public void releaseConnection() {
lock.lock();
try {
availableConnections++;
} finally {
lock.unlock();
}
}
}