在 Java 的多线程编程中,synchronized
和 AtomicInteger
都是用来实现线程安全的,但它们有着本质的区别。
一、实现原理不同
synchronized
是 Java 内置的同步机制,它通过对象的锁来实现对共享资源的独占式访问。当一个线程获取到对象的锁后,其他线程必须等待,直到锁被释放。它主要依赖于 JVM 的对象头中的锁标志位来进行实现。这种同步机制相对重量级,特别是在竞争激烈的场景下,线程频繁地获取和释放锁,会导致性能开销较大。
AtomicInteger
是基于 CAS(Compare-And-Swap)算法实现的原子类。CAS 是一种无锁算法,它通过硬件指令级别的原子操作来确保变量的更新是一个不可分割的整体,从而使多个线程能够并发地安全访问共享变量,而不需要依赖传统的锁机制。这使得 AtomicInteger
在性能上通常优于 synchronized。
二、使用场景不同
- 单一变量的原子操作
AtomicInteger
适用于对单个变量进行原子操作的场景,比如对一个计数器进行增减操作。例如,维护一个在线人数的计数器,多个线程同时在线或离线时,可以使用AtomicInteger
来安全地更新人数。
AtomicInteger onlineCount = new AtomicInteger(0); // 线程在线 onlineCount.incrementAndGet(); // 线程离线 onlineCount.decrementAndGet();
- 复杂同步逻辑
synchronized
更适合用于更复杂的同步场景,比如需要对多个变量进行同步操作,或者需要对一段代码进行同步控制。例如,在一个银行转账的场景中,涉及到两个账户的余额更新,此时可以使用 synchronized 来确保整个转账过程的原子性。
public class BankAccount { private double balance; public synchronized void transfer(BankAccount target, double amount) { if (this.balance >= amount) { this.balance -= amount; target.balance += amount; } } }
三、功能特性不同
- 可重入性
synchronized
是可重入的,这意味着一个线程在获取到锁后,可以再次获取相同的锁,而不会发生死锁。这在一定程度上提高了代码的可读性和可维护性。
public class ReentrantExample { private Object lock = new Object(); public void outerMethod() { synchronized (lock) { innerMethod(); } } public void innerMethod() { synchronized (lock) { // 执行一些操作 } } }
- 公平性
synchronized
不是公平锁。公平锁是指线程获取锁的顺序是按照线程申请锁的顺序来获取的,而非公平锁则可能会让后申请的线程先获取到锁。AtomicInteger
并不涉及公平性的问题,它主要是提供一种高效的原子操作方式。 - 锁的粒度控制
synchronized
在一定程度上可以灵活控制锁的粒度。可以通过合理地定义同步代码块或者同步方法来控制锁的范围,从而在保证线程安全的同时,提高程序的并发性能。
public class LockGranularityExample { private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void method1() { synchronized (lock1) { // 操作共享资源 1 } } public void method2() { synchronized (lock2) { // 操作共享资源 2 } } }
而 AtomicInteger
的锁粒度是固定的,它只针对单个变量进行原子操作,无法像 synchronized 那样灵活地控制锁的粒度。
在选择使用 synchronized
还是 AtomicInteger
时,需要根据具体的业务场景和性能需求来综合考虑。如果只是对单个变量进行简单的原子操作,AtomicInteger
通常是一个更好的选择;如果涉及到复杂的同步逻辑或者需要对多个变量进行同步,那么 synchronized
可能会更合适。