1

我有一个适用于所有用户的 bean,它将 ids-> 名称存储在&地图@ApplicationScoped中,反之亦然。Trovejava.util

我只是在构建 bean或(如果网站管理员手动刷新的情况下)构建地图一次。

在 bean 方法中,我只是将get()与地图一起使用,因此不修改地图。这将是线程安全的,因为它仅用于准备好的目的吗?我不会与外部的任何其他 bean 共享地图,也不会在我的代码中随时修改地图(添加/删除条目)。

另外,在这种情况下是否需要使字段成为最终字段?

豆代码如下:

@ApplicationScoped
@ManagedBean(name="directory", eager=true)
public class directory {

    private static TIntObjectHashMap<String> idsToNamesMap;
    private static TreeMap<String, Integer> namesToIdsMap;

    @PostConstruct
    public void buildDirectory(){
        // building directory here ....
    }

    public String getName(int topicId){
        return idsToNamesMap.get(topicId);
    }    

    public List<Entry<String, Integer>> searchTopicsByName(String query){
        return new ArrayList(namesToIdsMap.subMap(query, true, query+"z", true).entrySet());
    }        
}

4

2 回答 2

3

构建对象后可能存在可见性问题。也就是说,在您的构造函数调用之后,映射可能会显示为填充到填充它们的线程,但不一定填充到其他线程,至少不是马上。此类问题在Java Concurrency in Practice的第 3 章中进行了广泛讨论。但是,我认为如果您将地图声明为volatile

private static volatile TIntObjectHashMap<String> idsToNamesMap;
private static volatile TreeMap<String, Integer> namesToIdsMap;

你应该没问题。

更新

再次查看您的代码时,我刚刚意识到了一些事情。这些地图是static- 为什么它们被构造函数填充到实例上下文中?首先,它使读者感到困惑。其次,如果创建了多个对象实例,那么您将对映射进行额外的写入,而不仅仅是一个,可能在其他线程正在读取它们时。

您应该将它们设为非static,或将它们填充到静态初始化块中。

于 2012-04-11T23:53:57.230 回答
3

在这种情况下,您不必将它们声明为 volatile 或使用任何类型的同步进行保护。只要构造线程将构建它们并与主内存同步。

为此,构造线程只需对 volatile 变量进行一次写入或进入/退出同步锁。这将通过内存屏障,所有本地线程数据都将在主线程中。然后所有其他线程读取此数据将是安全的。

甚至更多 - 不必要的易失性或同步块 - 会导致严重的性能损失 - 每次访问变量时都会通过内存屏障 - 这是一项昂贵的操作

于 2012-04-12T00:00:06.143 回答