AtomicInteger的使用(高并发)

本文介绍了在高并发场景下,使用AtomicInteger解决计数器++操作的问题。通过对比普通代码和加volatile关键字的效果,展示AtomicInteger如何利用CAS(Compare and Swap)原理确保原子性,从而有效避免数据不一致。实验结果显示AtomicInteger成功解决了并发问题。

AtomicInteger的使用(高并发)

普通代码处理高并发,对count进行++操作:

public class MyAtomicInteger {
    private static final Integer threadCount = 20;
    private static Integer num = 0;
    private static void increase() {
        num++;
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int i1 = 0; i1 < 2000; i1++) {
                    increase();
                }
            });
            threads[i].start();
        }
        while (Thread.activeCount() > 1) {
//            意思就是调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。
//            但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
//            注意调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName());
        System.out.println("num:" + num);
    }
}

运行结果如下:

main
num:36939

正确值应该是40000,但是少了,说明我们的代码有问题
加上volatile修饰count:

public class MyAtomicInteger {
    private static final Integer threadCount = 20;
    private static volatile Integer num = 0;
    private static void increase() {
        num++;
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int i1 = 0; i1 < 2000; i1++) {
                    increase();
                }
            });
            threads[i].start();
        }
        while (Thread.activeCount() > 1) {
//            意思就是调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。
//            但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
//            注意调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName());
        System.out.println("num:" + num);
    }
}

结果还是差强人意,结果如下:

main
num:39490

这个时候引入java并发包下的AtomicInteger类,利用其原子操作实现高并发问题解决,代码如下:

public class MyAtomicInteger {
    private static final Integer threadCount = 20;
    private static AtomicInteger count = new AtomicInteger(0);
    private static void increase() {
        count.incrementAndGet();
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int i1 = 0; i1 < 2000; i1++) {
                    increase();
                }
            });
            threads[i].start();
        }
        while (Thread.activeCount() > 1) {
//            意思就是调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。
//            但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
//            注意调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName());
        System.out.println("num:" + count);
    }
}

运行结果:

main
num:40000

可见实现了对高并发情况下问题的解决!

实现原理:
利用cas原理,在主存中有一V值,线程在进入的时候会获取到V值,在进行更新的时候,会把自己获取到的V值和主存的V值进行比较,如果相等就进行更新操作,然后返回V的旧值,避免重复更新操作。同时和之前线程同时进来的线程获取的V值就不等于之前线程更新后主存中的V值了,就不能实现更新操作。但是,会利用while循环
while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); 不断地把主存中的V值更新成自己的V值,当相等的时候,实现更新操作,从而解决高并发问题。这就是无锁环境下的并发控制,效率高于qas锁,称为乐观锁。。

以上是博主的学习过程,如有不妥之处,望指正!!!!!

### AtomicInteger 解决并发问题的原理 `AtomicInteger` 类通过利用硬件级别的原子操作来提供线程安全的操作。具体来说,这类对象内部使用了比较并交换(Compare-and-Swap, CAS)机制[^2]。当多个线程尝试同时修改同一个 `AtomicInteger` 实例时,只有满足特定条件的一个线程能够成功成写入动作。 #### 原子性保障 对于整数类型的自增或其他基本运算而言,传统方式下这些看似简单的操作实际上是由读取、计算以及存储三个步骤组成的复合指令序列,在多线程环境下容易引发竞态条件(race condition)。而借助于 `compareAndSet()` 方法所提供的 CAS 功能,可以在不依赖的情况下确保每次更新都是基于最新的内存状态进行的: ```java public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } ``` 上述代码片段展示了如何实现一次典型的 CAS 调用过程——它会先检查当前的实际值是否与预期相符;若是则立即替换为目标数值,并返回真表示此次变更有效;反之则保持原状不变且反馈假给调用者[^3]。 ### 适用场景分析 由于其高效的无特性,`AtomicInteger` 特别适合用于那些频繁发生读/改需求但不需要复杂同步逻辑的应用场合。以下是几个典型例子: - **计数器**:统计访问次数或记录事件发生的频次。 - **ID生成器**:创建唯一编号如订单号、流水号等。 - **限流控制**:管理资源请求速率以防止过载。 下面是一个简单示例展示了一个基于 `AtomicInteger` 构建的任务队列长度监控工具: ```java import java.util.concurrent.atomic.AtomicInteger; class TaskQueueMonitor { private static final int MAX_QUEUE_SIZE = 10; private AtomicInteger queueSize = new AtomicInteger(); public synchronized void addTask() throws Exception { if (!queueSize.compareAndSet(queueSize.get(), Math.min(MAX_QUEUE_SIZE, queueSize.incrementAndGet()))) { throw new Exception("Queue is full"); } } public synchronized void removeTask(){ queueSize.decrementAndGet(); } public int getCurrentSize(){ return queueSize.get(); } } ``` 此案例中,每当有新的任务加入到队列里时都会触发 `addTask()` 函数,该函数运用了 `compareAndSet()` 来保证即使是在高并发情况下也能精确地维护队列大小不超过设定上限[^4]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值