使用Spring @Async和ThreadPoolTask​​Scheduler,pool-size = 1

时间:2013-06-24 14:44:58

标签: java spring asynchronous threadpool threadpoolexecutor

我们在基于Spring的Web应用程序中有一个服务实现,它增加了db中的一些统计计数器。由于我们不想弄乱用户的响应时间,因此我们使用Spring的@Async异步定义它们:

public interface ReportingService {

    @Async
    Future<Void> incrementLoginCounter(Long userid);

    @Async
    Future<Void> incrementReadCounter(Long userid, Long productId);
}

这样的spring任务配置:

<task:annotation-driven executor="taskExecutor" />
<task:executor id="taskExecutor" pool-size="10" />

现在,拥有pool-size="10",当两个线程尝试两个线程创建包含计数器的相同初始记录时,我们会遇到并发问题。

这里设置pool-size="1"以避免这些冲突是一个好主意吗?这有副作用吗?我们有很多地方可以触发异步操作来更新统计信息。

2 个答案:

答案 0 :(得分:7)

与单个线程处理它们的速度相比,副作用将取决于将任务添加到执行程序的速度。如果每秒添加的任务数大于单个线程在一秒钟内可以处理的数量,那么就会冒着队列随时间增加的风险,直到最终出现内存不足错误。

在此页Task Execution查看执行者部分。他们声称拥有一个无限制的队列并不是一个好主意。

如果您知道可以比添加任务更快地处理任务,那么您可能很安全。如果没有,您应该添加队列容量并在队列达到此大小时处理输入线程阻塞。

答案 1 :(得分:3)

查看您发布的两个示例,而不是@Async调用的常量流,考虑在客户端请求时更新JVM本地变​​量,然后让后台线程不时地将其写入数据库。沿着(介意半伪代码):

class DefaultReportingService implements ReportingService {

    ConcurrentMap<Long, AtomicLong> numLogins;

    public void incrementLoginCounterForUser(Long userId) {
        numLogins.get(userId).incrementAndGet();
    }

    @Scheduled(..)
    void saveLoginCountersToDb() {
        for (Map.Entry<Long, AtomicLong> entry : numLogins.entrySet()) {
            AtomicLong counter = entry.getValue();
            Long toBeSummedWithTheValueInDb = counter.getAndSet(0L);
            // ...
        }
    }   
}