如何使用双重检查锁定使此代码线程安全?

时间:2013-08-15 14:19:55

标签: java multithreading thread-safety double-checked-locking

单线程版本:

private final List<Element> list = new ArrayList<Element>();

public Element getElementAt(int index) {

    if (index >= list.size()) {
        for (int i = list.size(); i <= index; i++) {
            list.add(createElement(i));
        }
    }

    return list.get(index);
}

现在我正在尝试使用双重检查锁定创建一个线程安全的版本:

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;

...

private volatile List<Element> list = ImmutableList.of();

public Element getElementAt(int index) {

    if (index >= list.size()) {

        synchronized (this) {
            if (index >= list.size()) {
                Builder<Element> newListBuilder = ImmutableList.<Element> builder();
                newListBuilder.addAll(list);
                for (int i = list.size(); i <= index; i++) {
                    newListBuilder.add(createElement(i));
                }

                list = newListBuilder.build();
            }
        }
    }

    return list.get(index);
}

这是对的吗?

1 个答案:

答案 0 :(得分:0)

您正在做的更像是地图/字典查找。如果你认为你的列表真的像Map<Integer, Element>那样,那么你可以使用并发映射的putIfAbsent方法来处理它而不会阻塞:

private final ConcurrentMap<Integer, Element> map =
                new ConcurrentHashMap<Integer, Element>();

public Element getElementAt(int index) {

    if (index >= map.size()) {
        for (int i = map.size(); i <= index; i++) {
            map.putIfAbsent(i, createElement());
        }
    }

    return map.get(index);
}

这假设对createElement返回的元素没有特定的排序要求 - 但如果是这种情况,那么无论如何都需要对它们的创建方式有更严格的约束。

虽然实际上你可以简单地在这个方法周围粘贴一个同步块(除了访问列表之外)。这很容易理解,而且速度相对较快。如果获取元素不是代码的性能热点,我不想做一些“聪明”的事情只是为了减少几纳秒。