Java里如何实现线程安全的计数与限流

来源:AI智能体作者:BIT程序员头衔:程序员
导读:本期聚焦于小伙伴创作的《Java里如何实现线程安全的计数与限流》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Java里如何实现线程安全的计数与限流》有用,将其分享出去将是对创作者最好的鼓励。

在Java多线程编程中,计数和限流是非常高频的需求,比如统计接口调用次数、限制单位时间内的请求量等。如果直接使用普通的int变量进行计数,在多线程并发修改时会出现竞态条件,导致计数结果不准确,进而让限流逻辑失效。因此需要通过合理的方式实现线程安全的计数,再基于计数能力完成限流功能。

Java里如何实现线程安全的计数与限流

线程安全计数的实现方案

1. 使用AtomicInteger原子类

Java并发包中的AtomicInteger是基于CAS(Compare And Swap)机制实现的原子操作类,能够在无锁的情况下保证计数的线程安全,性能通常优于同步锁方案。

以下是使用AtomicInteger实现线程安全计数的示例:

import java.util.concurrent.atomic.AtomicInteger;

public class SafeCounter {
    // 初始化计数为0
    private AtomicInteger count = new AtomicInteger(0);

    // 自增计数并返回自增后的值
    public int increment() {
        return count.incrementAndGet();
    }

    // 获取当前计数
    public int getCount() {
        return count.get();
    }

    // 重置计数
    public void reset() {
        count.set(0);
    }

    public static void main(String[] args) throws InterruptedException {
        SafeCounter counter = new SafeCounter();
        // 创建10个线程,每个线程自增1000次
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.increment();
                }
            });
            threads[i].start();
        }
        // 等待所有线程执行完成
        for (Thread thread : threads) {
            thread.join();
        }
        // 输出最终计数,结果应为10000
        System.out.println("最终计数:" + counter.getCount());
    }
}

2. 使用synchronized同步锁

如果不想使用原子类,也可以通过synchronized关键字对计数操作加锁,保证同一时间只有一个线程能修改计数变量,从而实现线程安全。

示例代码如下:

public class SyncCounter {
    private int count = 0;

    // 对自增方法加锁
    public synchronized void increment() {
        count++;
    }

    // 获取计数也需要加锁,避免读到未刷新的缓存值
    public synchronized int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        SyncCounter counter = new SyncCounter();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.increment();
                }
            });
            threads[i].start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println("最终计数:" + counter.getCount());
    }
}

基于线程安全计数实现限流

常见的限流场景是限制单位时间内的请求次数,比如1秒内最多允许100次请求。我们可以结合线程安全的计数和时间判断来实现简单的固定窗口限流。

以下是基于AtomicInteger实现的固定窗口限流示例:

import java.util.concurrent.atomic.AtomicInteger;

public class SimpleRateLimiter {
    // 限流阈值,单位时间允许的最大请求数
    private final int limit;
    // 单位时间长度,单位毫秒
    private final long windowMillis;
    // 当前窗口内的计数
    private AtomicInteger count;
    // 当前窗口的起始时间
    private long windowStart;

    public SimpleRateLimiter(int limit, long windowMillis) {
        this.limit = limit;
        this.windowMillis = windowMillis;
        this.count = new AtomicInteger(0);
        this.windowStart = System.currentTimeMillis();
    }

    // 尝试获取请求许可,返回true表示允许请求,false表示被限流
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        // 判断当前是否在窗口内
        if (now - windowStart < windowMillis) {
            // 窗口内计数自增,判断是否超过阈值
            int currentCount = count.incrementAndGet();
            return currentCount <= limit;
        } else {
            // 窗口过期,重置窗口和计数
            synchronized (this) {
                // 双重检查,避免多个线程同时重置
                if (now - windowStart >= windowMillis) {
                    windowStart = now;
                    count.set(0);
                }
            }
            // 重置后重新尝试获取
            return tryAcquire();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 1秒内最多允许5次请求
        SimpleRateLimiter limiter = new SimpleRateLimiter(5, 1000);
        // 模拟10次请求
        for (int i = 0; i < 10; i++) {
            boolean allowed = limiter.tryAcquire();
            System.out.println("第" + (i + 1) + "次请求:" + (allowed ? "允许" : "被限流"));
            Thread.sleep(100);
        }
    }
}

两种计数方案的选择建议

如果计数场景是简单的自增、自减操作,优先选择AtomicInteger,它的无锁特性在高并发场景下性能更好。如果计数逻辑比较复杂,需要多个变量配合修改,或者需要保证多个操作的整体原子性,使用synchronized同步锁会更合适,代码逻辑也更清晰。

对于限流场景,上述固定窗口限流实现比较简单,但存在窗口边界突刺的问题,如果需要更平滑的限流效果,可以结合AtomicInteger实现滑动窗口限流,或者使用令牌桶、漏桶等更成熟的限流算法,不过核心的计数部分依然需要保证线程安全。

Java线程安全计数限流AtomicInteger修改时间:2026-06-30 15:36:31

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。