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

Java 如何使用双重检查锁定使此代码线程安全?,java,multithreading,thread-safety,double-checked-locking,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

单线程版本:

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);
}
private final List=new ArrayList();
公共元素getElementAt(int索引){
如果(索引>=list.size()){
对于(int i=list.size();i=list.size()){
已同步(此){
如果(索引>=list.size()){
Builder newListBuilder=ImmutableList.Builder();
newListBuilder.addAll(列表);

对于(int i=List.siz()),i您所做的更像是一个地图/字典查找。如果您认为您的列表实际上像<代码> map < /C> >,那么您可以使用并发映射的PuffIess方法来处理此过程而不阻塞:

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);
}
私有最终ConcurrentMap映射=
新的ConcurrentHashMap();
公共元素getElementAt(int索引){
如果(索引>=map.size()){

对于(int i=map.size();我认为这行不通。你需要一个完全锁、不可变或无锁的数据结构。看看如何用
集合包装它。synchronizedList
或使用
CopyOnWriteArrayList
?你为什么不使用
synchronized
?你需要节省几纳秒吗?令人惊讶的是,人们仍然认为同步不知何故,速度很慢,即使完全没有迹象表明存在瓶颈或它会有一点帮助,也会经历非常复杂的设计。将此问题标记为重复的人指向一个完全不同的问题。那里没有答案。我不太确定是否使用地图而不是列表。如果我可以的话我想知道的第一个问题是:“为什么作者没有使用列表?”因为map.putIfAbsent(I,createElement())不是一个原子操作,createElement()也可能会创建一个额外的Element实例,你看到ConcurrentHashMap.size()的用法了吗实施了吗?这是一项昂贵的任务operation@MattBall我同意你的观点。在实践中,适用性将取决于为什么这需要密集-正如我所写的那样,我看不出为什么我们不能按需创建每个元素。在某些情况下,使用列表索引可能是一个次要的解决办法,它们可能是一些对象的ID,可能是直接用作映射键。@orionll-如果不是原子的,则表示此行将调用
createElement
两次,情况并非如此。它将与两行相同
Element e=createElement();map.putIfAbsent(i,e);
。如果是指两个线程同时调用,则每个线程都可以创建一个元素(只有一个进入映射)然后是的-这完全是非阻塞方法的要点。避免这种情况的唯一方法是使整个方法同步,因此第二个线程等待第一个线程完成。
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);
}