如何在Java中返回线程安全/不可变集合?

时间:2011-07-12 01:45:47

标签: java list thread-safety synchronized unmodifiable

在我编写的项目中,我需要从函数返回一个线程安全且不可变的视图。但是,我不确定这一点。由于synchronizedListunmodifiableList只返回列表的视图,因此我不知道是否

Collections.synchronizedList(Collections.unmodifiableList(this.data));

会做到这一点。

有人能告诉我这是否正确,如果不是,有什么情况可能会失败吗?

感谢您的任何意见!

6 个答案:

答案 0 :(得分:13)

我发现这是JDK中的一个真正的差距。幸运的是,由Java Collections设计师Joshua Bloch领导的Google团队创建了一个包含真正不可变集合的library

ImmutableList特别是您正在寻找的实施方案。 Here是番石榴ImmutableCollections的一些特征的快速草图。

答案 1 :(得分:5)

我认为不可修改就足够了。你不能写它,这是导致多线程访问问题的原因。它是只读的,所以我不需要额外的同步步骤。

如果有这样的问题,最好查看源代码。看起来它会返回UnmodifiableList

/**
 * @serial include
 */
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                  implements List<E> {
    static final long serialVersionUID = -283967356065247728L;
final List<? extends E> list;

UnmodifiableList(List<? extends E> list) {
    super(list);
    this.list = list;
}

public boolean equals(Object o) {return o == this || list.equals(o);}
public int hashCode()       {return list.hashCode();}

public E get(int index) {return list.get(index);}
public E set(int index, E element) {
    throw new UnsupportedOperationException();
    }
public void add(int index, E element) {
    throw new UnsupportedOperationException();
    }
public E remove(int index) {
    throw new UnsupportedOperationException();
    }
public int indexOf(Object o)            {return list.indexOf(o);}
public int lastIndexOf(Object o)        {return list.lastIndexOf(o);}
public boolean addAll(int index, Collection<? extends E> c) {
    throw new UnsupportedOperationException();
    }
public ListIterator<E> listIterator()   {return listIterator(0);}

public ListIterator<E> listIterator(final int index) {
    return new ListIterator<E>() {
    ListIterator<? extends E> i = list.listIterator(index);

    public boolean hasNext()     {return i.hasNext();}
    public E next()          {return i.next();}
    public boolean hasPrevious() {return i.hasPrevious();}
    public E previous()      {return i.previous();}
    public int nextIndex()       {return i.nextIndex();}
    public int previousIndex()   {return i.previousIndex();}

    public void remove() {
        throw new UnsupportedOperationException();
            }
    public void set(E e) {
        throw new UnsupportedOperationException();
            }
    public void add(E e) {
        throw new UnsupportedOperationException();
            }
    };
}

答案 2 :(得分:4)

Collections.unmodifiableList(this.data) 

会这样做,因为它会返回一个视图。对此视图的任何修改尝试都将导致UnsupportedOperationException被抛出。以下是Collections#unmodifiableList文档的摘录。

  

返回指定列表的不可修改视图。此方法允许模块为用户提供对内部列表的“只读”访问。对返回列表的查询操作“读取”到指定列表,并尝试修改返回的列表,无论是直接还是通过其迭代器,都会导致UnsupportedOperationException。

     

...

java 8 java.util.Collections javadoc

答案 3 :(得分:3)

tslint.json

是的,现在已内置在Java 10及更高版本中。

每一个都返回原始对象中单独的对象集合。返回的集合是 not 原始视图的视图,例如{ "linterOptions": { "exclude": [ "*.json", "**/*.json" ] } } 实用程序类方法。

答案 4 :(得分:0)

这些视图不会返回真正的线程安全集合。总有可能有人会修改后备集合或集合中的元素。

要解决此问题,您需要使用不可变集合和不可变元素。然后,线程安全就会发生。

Clojure包含这样的不可变(或持久)collections

简单地说,添加或删除新元素会返回一个新集合,通常 通过巧妙使用Trie类型数据结构来重用旧集合的大部分内容。

就他们自己而言,这些不适合直接使用Java。

Pure4j试图将这些(以及Clojure提倡的基于不可变/值的样式)移植到Java语言中。这可能就是你想要的。

免责声明:我是Pure4J的开发者

答案 5 :(得分:0)

Java 9+ ImmutableCollections 是线程安全的。例如,List.of, Map.of, Map.copyOf(Java 10+)... 根据oracle doc,

<块引用>

不可变集合的一个优点是它是自动线程安全的。创建集合后,可以将其交给多个线程,它们都会看到一致的视图。

阅读更多信息:oracle docs