likes
comments
collection
share

原子包-整数AtomicInteger

作者站长头像
站长
· 阅读数 25

官方文档-AtomicInteger

public class AtomicInteger
extends Number
implements Serializable

An int value that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. An AtomicInteger is used in applications such as atomically incremented counters, and cannot be used as a replacement for an Integer. However, this class does extend Number to allow uniform access by tools and utilities that deal with numerically-based classes. 可以自动更新的 int 值。有关原子变量属性的描述,请参阅 java.util.concurrent.atomic 包规范。 AtomicInteger 用于原子递增计数器等应用程序,不能用作 Integer 的替代品。但是,此类确实扩展了 Number 以允许处理基于数字的类的工具和实用程序进行统一访问。

AtomicInteger作用

AtomicInteger是Java中的一个类,它位于java.util.concurrent.atomic包下,主要用于在多线程环境中对整数进行原子操作。它的主要作用是提供了一种线程安全的方式来进行整数的增加、减少和更新操作,而不需要显式地使用锁来保护这些操作。

主要的作用包括:

  1. 原子性操作: AtomicInteger提供了一系列原子操作方法,如incrementAndGet()(增加并获取)、decrementAndGet()(减少并获取)、getAndIncrement()(获取并增加)和getAndDecrement()(获取并减少),这些操作在多线程环境中是原子的,不会出现竞态条件。

  2. 无锁性能: 相比于使用传统的锁来保护共享整数的操作,AtomicInteger使用了无锁的方式,因此性能更高。在高并发情况下,它通常比使用锁的方式更有效率。

  3. 线程安全: AtomicInteger内部使用了CAS(Compare-And-Swap)操作来保证线程安全。CAS是一种硬件级别的原子性操作,它可以确保在多线程环境中对整数的操作不会产生竞态条件。

  4. 可用于计数器: AtomicInteger经常用于实现计数器,例如统计某个操作的执行次数或者实现并发的计数功能。

总之,AtomicInteger的主要作用是提供一种高效、线程安全的方式来操作整数,特别适用于多线程环境下需要对整数进行原子操作的场景。

AtomicInteger应用场景

AtomicInteger在Java中有很多应用场景,特别适用于需要在多线程环境中进行原子性操作的情况。以下是一些常见的应用场景:

  1. 计数器: AtomicInteger常用于实现计数器,例如统计某个操作的执行次数或者记录某个事件发生的次数。多个线程可以并发地增加或减少计数器的值,而不会发生竞态条件。

  2. 唯一标识生成器: 在分布式系统中,生成唯一标识符(如订单号、交易号等)是一个常见的需求。AtomicInteger可以用来生成唯一的整数标识,每个线程可以原子地获取下一个标识符。

  3. 线程池控制: 当需要控制线程池中的线程数量时,可以使用AtomicInteger来动态地增加或减少活动线程的数量,以适应不同的工作负载。

  4. 并发集合的大小控制: 在使用一些并发集合(如ConcurrentHashMap、ConcurrentLinkedQueue)时,有时需要知道集合的大小。AtomicInteger可以用于跟踪集合中元素的数量,而不需要使用锁来保护。

  5. 性能统计: 在性能测试和性能分析中,可以使用AtomicInteger来记录各种性能指标,如响应时间、请求数量等,以实时跟踪系统的性能。

  6. 限流控制: 用于限制某个操作或资源的访问速率,例如在API调用中限制请求的速率,可以使用AtomicInteger来跟踪已经处理的请求数量,并在达到限制时拒绝额外的请求。

  7. 状态标志: 可以使用AtomicInteger作为状态标志,例如表示某个任务的状态,不同的数值代表不同的状态,而多个线程可以原子地更新和读取这个状态。

总之,AtomicInteger在需要进行原子性操作的多线程应用中非常有用。它可以帮助确保线程安全,并提供高效的方式来进行整数的增加、减少和更新操作。

举例

以下是一些使用AtomicInteger的示例,以说明其在不同应用场景下的用途:

  1. 计数器示例:
import java.util.concurrent.atomic.AtomicInteger;

public class CounterExample {
    public static void main(String[] args) {
        AtomicInteger counter = new AtomicInteger(0);

        // 多个线程增加计数器的值
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.incrementAndGet();
                }
            }).start();
        }

        // 等待所有线程完成
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final Counter Value: " + counter.get()); // 应该输出10000
    }
}
  1. 唯一标识生成器示例:
import java.util.concurrent.atomic.AtomicInteger;

public class UniqueIdGenerator {
    private static final AtomicInteger counter = new AtomicInteger(0);

    public static int getNextId() {
        return counter.incrementAndGet();
    }

    public static void main(String[] args) {
        System.out.println("Unique ID 1: " + UniqueIdGenerator.getNextId());
        System.out.println("Unique ID 2: " + UniqueIdGenerator.getNextId());
        System.out.println("Unique ID 3: " + UniqueIdGenerator.getNextId());
    }
}
  1. 限流控制示例:
import java.util.concurrent.atomic.AtomicInteger;

public class RateLimiter {
    private static final int MAX_REQUESTS_PER_SECOND = 100;
    private static final AtomicInteger requestCounter = new AtomicInteger(0);

    public static boolean allowRequest() {
        int currentRequests = requestCounter.incrementAndGet();
        if (currentRequests <= MAX_REQUESTS_PER_SECOND) {
            return true;
        } else {
            requestCounter.decrementAndGet();
            return false;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 150; i++) {
            if (RateLimiter.allowRequest()) {
                System.out.println("Request #" + (i + 1) + " allowed.");
            } else {
                System.out.println("Request #" + (i + 1) + " rejected.");
            }
        }
    }
}

这些示例演示了AtomicInteger在计数、唯一标识生成和限流控制等不同应用场景中的使用方式。它们都能够在多线程环境中提供线程安全的整数操作。

源码分析

AtomicInteger

主要看一下自增方法

java.util.concurrent.atomic.AtomicInteger#incrementAndGet

/**
 * Atomically increments by one the current value.
 *
 * @return the updated value
 */
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1; //cas数据是线程安全,那么cas数据 + 1仍然安全,因为线程安全的本质是确保可变更数据是线程安全的,只要可变更数据是线程安全,那么加一个常量值仍然线程安全
}

本质其实就是当前数据 + 1


数据字段

private volatile int value; //注意,是volatile

这个就是当前数据。注意,加了volatile。为什么要volatile?因为并发包解决线程安全,都是cas结合volatile。二者缺一不可,一个是解决写,一个是解决读。

Unsafe

sun.misc.Unsafe#getAndAddInt

/**
 * Atomically adds the given value to the current value of a field
 * or array element within the given object <code>o</code>
 * at the given <code>offset</code>.
 *
 * @param o object/array to update the field/element in
 * @param offset field/element offset
 * @param delta the value to add
 * @return the previous value
 * @since 1.8
 */
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!compareAndSwapInt(o, offset, v, v + delta));
    return v;
}

原子类的底层,都是基于Unsafe的cas。和AQS一样。

incrementAndGet为什么可以确保线程安全?

incrementAndGet() 方法是 AtomicInteger 类的一个原子操作方法,它能够确保线程安全,主要基于以下原因:

  1. CAS (Compare-And-Swap) 操作: incrementAndGet() 方法内部使用了CAS操作。CAS是一种硬件级别的原子性操作,它能够在不使用锁的情况下,实现对共享变量的原子更新。CAS操作包括三个步骤:比较当前值、如果相等则更新,否则重试。这个过程是原子的,如果多个线程同时调用 incrementAndGet(),只有一个线程能够成功,其他线程会自动重试。

  2. 保证操作的原子性: incrementAndGet() 方法内部会使用CAS操作来增加 AtomicInteger 的值,并返回增加后的新值。这个操作是原子的,即使多个线程同时调用,也不会导致竞态条件。

  3. 无锁操作: 与传统的锁机制不同,CAS操作是一种无锁操作,不会导致线程阻塞。这意味着即使有多个线程同时调用 incrementAndGet(),它们也不会被阻塞,而是会竞争更新操作,确保只有一个线程能够成功。

  4. 线程安全性: 由于CAS操作和无锁特性,AtomicIntegerincrementAndGet() 方法能够保证多线程环境下的线程安全性。多个线程可以同时调用这个方法,而不会破坏数据的一致性或导致数据竞争问题。

总之,incrementAndGet() 方法之所以能够确保线程安全,是因为它内部使用CAS操作来保证对整数值的原子增加,并且不需要显式地使用锁,因此能够在高并发环境中高效地工作。这使得 AtomicInteger 成为处理多线程并发情况下整数操作的有力工具。

unsafe.getAndAddInt是线程安全,但是+1还是线程安全吗?

unsafe.getAndAddInt 是线程安全的,因为它是通过CAS(Compare-And-Swap)操作来实现的,确保了在多线程环境下的原子性。

然而,+1 操作并不是线程安全的,因为它不是原子操作。当多个线程同时执行 +1 操作时,会存在竞态条件,可能导致结果不符合预期。

AtomicInteger 类的 incrementAndGet() 方法中,它的实现结合了 unsafe.getAndAddInt+1 操作,但整个方法是线程安全的。这是因为 getAndAddInt 部分确保了原子性的增加,而后面的 +1 只是将结果加1并返回,这个步骤不会破坏原子性。整个方法的原子性是由 getAndAddInt 确保的,因此 incrementAndGet() 是线程安全的。

如果在不使用 AtomicInteger 的情况下,想要实现线程安全的整数增加,可以考虑使用锁或其他并发工具来保护 +1 操作,以避免竞态条件。但是需要注意,锁的使用可能会带来一定的性能开销,特别是在高并发情况下。因此,如果只是需要对整数进行原子增加操作,AtomicInteger 是更高效的选择。


cas数据是线程安全,那么cas数据 + 1仍然安全,因为线程安全的本质是确保可变更数据是线程安全的,只要可变更数据是线程安全,那么加一个常量值仍然线程安全。

总结

说白了,就是线程安全版本的int,并且也在并发包里面,并且也是基于cas和volatile。