跳转至

Java AtomicLong:深入理解与高效应用

简介

在多线程编程中,对共享变量的操作往往需要特别小心,以避免数据竞争和不一致的问题。AtomicLong 是 Java 并发包 java.util.concurrent.atomic 中的一个类,它提供了一种线程安全的方式来操作长整型变量。本文将深入探讨 AtomicLong 的基础概念、使用方法、常见实践以及最佳实践,帮助读者更好地在多线程环境中运用这一强大工具。

目录

  1. 基础概念
  2. 使用方法
    • 创建 AtomicLong 对象
    • 常用方法介绍
  3. 常见实践
    • 多线程计数器
    • 原子更新场景
  4. 最佳实践
    • 性能优化
    • 避免不必要的同步
  5. 小结
  6. 参考资料

基础概念

AtomicLong 是一个对长整型进行原子操作的类。原子操作意味着这些操作在多线程环境下不会被其他线程干扰,能够保证操作的完整性。与普通的 long 类型变量不同,AtomicLong 提供了一系列的方法,这些方法能够确保在多线程环境下对变量的读写和修改操作都是原子性的,从而避免了数据竞争和不一致的问题。

使用方法

创建 AtomicLong 对象

可以通过以下两种方式创建 AtomicLong 对象:

// 方式一:创建初始值为 0 的 AtomicLong 对象
AtomicLong atomicLong1 = new AtomicLong();

// 方式二:创建带有指定初始值的 AtomicLong 对象
AtomicLong atomicLong2 = new AtomicLong(100);

常用方法介绍

  • get():获取当前 AtomicLong 的值。
AtomicLong atomicLong = new AtomicLong(10);
long value = atomicLong.get();
System.out.println("当前值: " + value);
  • set(long newValue):设置 AtomicLong 的值。
atomicLong.set(20);
value = atomicLong.get();
System.out.println("设置后的值: " + value);
  • incrementAndGet():先将值加 1,然后返回新的值。
long incrementedValue = atomicLong.incrementAndGet();
System.out.println("自增后的值: " + incrementedValue);
  • getAndIncrement():先返回当前值,然后将值加 1。
long previousValue = atomicLong.getAndIncrement();
System.out.println("自增前的值: " + previousValue);
  • addAndGet(long delta):将指定的值加到当前值上,并返回新的值。
long addedValue = atomicLong.addAndGet(5);
System.out.println("相加后的值: " + addedValue);
  • getAndAdd(long delta):先返回当前值,然后将指定的值加到当前值上。
previousValue = atomicLong.getAndAdd(5);
System.out.println("相加前的值: " + previousValue);

常见实践

多线程计数器

在多线程环境下,使用普通的 long 变量作为计数器会导致数据不一致的问题。而 AtomicLong 可以很好地解决这个问题。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class AtomicLongCounter {
    private static AtomicLong counter = new AtomicLong(0);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 1000; i++) {
            executorService.submit(() -> {
                counter.incrementAndGet();
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);

        System.out.println("最终计数器的值: " + counter.get());
    }
}

在上述代码中,创建了一个 AtomicLong 类型的计数器 counter,并在多个线程中对其进行自增操作。由于 incrementAndGet() 方法的原子性,最终的计数器值是准确的。

原子更新场景

在某些情况下,需要根据当前值进行条件更新。AtomicLongcompareAndSet(long expect, long update) 方法可以实现这一功能。该方法会比较当前值是否等于预期值 expect,如果相等,则将值更新为 update,并返回 true;否则返回 false

AtomicLong atomicLong = new AtomicLong(10);
boolean result = atomicLong.compareAndSet(10, 20);
System.out.println("更新结果: " + result);
System.out.println("当前值: " + atomicLong.get());

最佳实践

性能优化

在高并发场景下,频繁地对 AtomicLong 进行操作可能会带来一定的性能开销。可以考虑使用 LongAdder 类,它在高并发下的性能通常比 AtomicLong 更好。LongAdder 内部采用了分段累加的方式,减少了竞争。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;

public class LongAdderExample {
    private static LongAdder longAdder = new LongAdder();

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 1000; i++) {
            executorService.submit(() -> {
                longAdder.increment();
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);

        System.out.println("最终 LongAdder 的值: " + longAdder.sum());
    }
}

避免不必要的同步

虽然 AtomicLong 本身是线程安全的,但如果在代码中对其进行了不必要的同步操作,可能会降低性能。例如,不要在同步块中对 AtomicLong 进行简单的读取或修改操作,因为这些操作本身已经是原子性的。

// 不好的做法
synchronized (this) {
    atomicLong.incrementAndGet();
}

// 好的做法
atomicLong.incrementAndGet();

小结

AtomicLong 为多线程编程中的长整型变量操作提供了一种简单而有效的线程安全解决方案。通过使用 AtomicLong 的各种原子操作方法,可以避免数据竞争和不一致的问题。在实际应用中,根据具体的场景选择合适的类(如 AtomicLongLongAdder),并遵循最佳实践,能够提高代码的性能和稳定性。

参考资料