如何确保传递给方法的集合的同步?

时间:2017-04-03 17:40:36

标签: java android multithreading

我在内部类上有以下方法,我用它来对多线程应用程序中的多个集合进行排序。我想确保每个集合在排序时都是同步的(我已经在我迭代的其他地方同步)。

注意:其中一个集合支持ListView的适配器。

private void sort(List<...> collection) {
    if (collection != null) {
        Collections.sort(collection, ...);
    }
}

问题是如果我将synchronised(collection) { Collections.sort }换行,IDE会警告我在方法参数上进行同步是不安全的。

我知道这是一个新参考,但参考仍然指向原始集合,这样安全吗?如果没有,有没有办法测试集合是否被锁定? (如果误解,我可以抛出异常)

2 个答案:

答案 0 :(得分:3)

我认为这里的问题是如何访问这些集合。 例如,如果您想要对集合进行多次读取和只有一个写入线程,则应使用包装类和ReentrantReadWriteLock:

public class ListWrapper {

    private final ArrayList<String> theList = new ArrayList<String>();
    private final ReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();    

    public String read() {
        r.lock();
        try {
            System.out.println("reading");
            if(theList.isEmpty()) {
                return null;
            }
            else {
                return theList.get(0);
            }
        } finally {
            r.unlock();
        }
    }

    public void write(String data) {
        w.lock();
        try {
            System.out.println("Written " + data);
            theList.add(data);
        } finally{
            w.unlock();
        }
    }
}

与包含内部列表的包装类相同,并且不直接公开列表。

public class ListWrapper {    
    private final List<String> obj;     
    public ListWrapper (Object obj) { 
        this.obj = obj; 
    }     

    public List<String> getList() { 
        return obj; 
    } 
}

上面的类是线程安全的,因为它不对包装的对象执行任何操作。 由类包装的对象可能是也可能不是线程安全的,因为在返回包装对象的引用之后,多个线程可以对该对象进行操作。

因此,包装对象本身是否是线程安全的,取决于包装对象的类是如何实现的。

您也可以使用

  

的CopyOnWriteArrayList

其中是ArrayList的线程安全变体,其中所有的变异操作都是通过创建底层数组的新副本来实现的。

这取决于我认为的实施。

答案 1 :(得分:-1)

我能给你的最好建议是在java.util.concurrent package中使用一个集合,并将该集合传递给你的方法。无论在代码中修改它们的位置,这些并发集合都会自动同步读/写(取决于集合)。我建议您阅读每个并发集合优化的内容,并根据您的应用程序选择一个。

另一个选项是将同步包装器传递给您可以创建的列表:

List list = Collections.synchronizedList(new ArrayList(...));

这基本上包装了集合的方法并使它们同步。传递返回的列表并调用它的成员函数可确保它们被串行调用。

编辑:

或者,我建议您创建自己的Collection(可能是ConcurrentSortableCollection),在排序之前自己进行同步。仅当并发集合中没有一个符合您的需要并且您需要进行特殊优化时,这才有用。

您也可以使排序方法静态同步,或者在调用排序(集合)方法之前简单地同步传入的集合,这可能更容易:

synchronized(collection) {
   sort(collection);
}