24

我需要编写一个 bean 来作为它被访问次数的计数器。

我正在考虑使用这样的@ApplicationScopedbeanAtomicInteger

@ApplicationScoped
class VisitsCounter {

    private AtomicInteger counter;

    @PostConstruct
    public void construct() {
        counter = new AtomicInteger(0);
    }

    public int visited() {
        return counter.incrementAndGet();
    }
}

我的问题是:同时考虑多个请求可以吗?还是我需要玩@ConcurrencyManagement@Lock注释?我想这Atomic*应该可以解决问题,但我不确定。

当我将线程安全集合作为字段时,同样适用吗?例如说我有

@ApplicationScoped
class ValuesHolder {

    private List<String> values;

    @PostConstruct
    public void construct() {
        values = Collections.synchronizedList(new LinkedList<String>());
    }

    public void insert(String value) {
        values.add(value);
    }

    public String remove(String value) {
        return values.remove(value);
    }
}

这些操作真的是线程安全的吗?

据说在修改bean的状态时应该使用并发注释和锁,但是如果我的列表已经照顾了线程安全怎么办?

4

1 回答 1

41

在 CDI 中,您没有并发管理,因此@ApplicationScoped只需声明注入对象的基数(即指示注入引擎只创建 bean 的一个实例并在所有应用程序中使用它)。它不会在 EJB 中转换您的 bean,也不会强制执行任何并发约束。

因此,虽然示例中的操作本质上是线程安全的,但由于AtomicInteger和 同步列表,通常情况并非如此。

一般来说,您可以:

  • 通过标准并发原语手动同步列表访问(正如您所做的那样)

  • 或者使用javax.ejb.Singleton注解,它指示应用服务器管理并发。这会将您的 bean 转换为 EJB,并且默认情况下强制执行@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)@Lock(LockType.WRITE).

顺便说一句,@ConcurrencyManagement并且 @Lock仅在单例会话 bean 上可用。

于 2013-01-10T12:44:26.053 回答