我在内部类上有以下方法,我用它来对多线程应用程序中的多个集合进行排序。我想确保每个集合在排序时都是同步的(我已经在我迭代的其他地方同步)。
注意:其中一个集合支持ListView的适配器。
private void sort(List<...> collection) {
if (collection != null) {
Collections.sort(collection, ...);
}
}
问题是如果我将synchronised(collection) { Collections.sort }
换行,IDE会警告我在方法参数上进行同步是不安全的。
我知道这是一个新参考,但参考仍然指向原始集合,这样安全吗?如果没有,有没有办法测试集合是否被锁定? (如果误解,我可以抛出异常)
答案 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);
}