Java 如何确保传递给方法的集合的同步?
我在一个内部类上有以下方法,我使用该方法对多线程应用程序中的多个集合进行排序。我想确保在排序时同步每个集合(我已经在迭代的其他地方同步了) 注意:其中一个集合支持ListView的适配器Java 如何确保传递给方法的集合的同步?,java,android,multithreading,Java,Android,Multithreading,我在一个内部类上有以下方法,我使用该方法对多线程应用程序中的多个集合进行排序。我想确保在排序时同步每个集合(我已经在迭代的其他地方同步了) 注意:其中一个集合支持ListView的适配器 private void排序(列表集合){ if(集合!=null){ 集合。排序(集合,…); } } 问题是如果我包装synchronized(collection){Collections.sort}IDE会警告我在方法参数上进行同步是不安全的 我知道这是一个新的参考,但参考仍然指向原始收藏,所以这是安
private void排序(列表集合){
if(集合!=null){
集合。排序(集合,…);
}
}
问题是如果我包装synchronized(collection){Collections.sort}
IDE会警告我在方法参数上进行同步是不安全的
我知道这是一个新的参考,但参考仍然指向原始收藏,所以这是安全的吗?如果没有,是否有方法测试集合是否已锁定?(因此,如果使用不当,我可以抛出一个异常)我能给您的最好建议是使用
java.util.concurrent
中的集合,并将该集合传递给您的方法。这些并发集合会自动同步读/写(取决于集合),而不管它们在代码中被修改到什么位置。我建议您阅读每个并发集合优化的内容,并根据您的应用程序选择一个
另一个选项是将同步包装器传递到列表中,您可以这样创建:
List list = Collections.synchronizedList(new ArrayList(...));
这基本上包装了集合的方法并使它们同步。传递返回的列表并调用其成员函数可确保它们被串行调用
编辑:
或者,我建议您创建自己的集合(可能是ConcurrentSortableCollection),在排序之前对其自身进行同步。只有当所有并发集合都不符合您的需要并且需要特殊优化时,这才有用
您还可以使排序方法静态同步,或者在调用排序(集合)方法之前对传入的集合进行同步,这可能更容易:
synchronized(collection) {
sort(collection);
}
我认为这里的问题是如何访问这些集合。 例如,如果希望对集合执行多个读取而只执行一个写入线程,则应使用包装类和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();
}
}
}
公共类ListWrapper{
private final ArrayList theList=新的ArrayList();
private final ReadWriteLock rwl=new ReentrantReadWriteLock();
私有最终锁r=rwl.readLock();
私有最终锁w=rwl.writeLock();
公共字符串读取(){
r、 锁();
试一试{
系统输出打印项次(“读取”);
if(list.isEmpty()){
返回null;
}
否则{
返回列表。获取(0);
}
}最后{
r、 解锁();
}
}
公共无效写入(字符串数据){
w、 锁();
试一试{
System.out.println(“写入的”+数据);
添加(数据);
}最后{
w、 解锁();
}
}
}
包装器类也是如此,它内部有列表,并且不直接公开列表
public class ListWrapper {
private final List<String> obj;
public ListWrapper (Object obj) {
this.obj = obj;
}
public List<String> getList() {
return obj;
}
}
公共类ListWrapper{
私人最终清单obj
公共ListWrapper(对象对象对象){
this.obj=obj;
}
公共列表getList(){
返回obj;
}
}
上面的类是线程安全的,因为它不会对包装的对象执行任何操作。
类包装的对象可能是线程安全的,也可能不是线程安全的,因为在返回包装对象的引用后,多个线程可以对该对象进行操作
因此,包装对象本身是否是线程安全的取决于包装对象的类是如何实现的
你也可以使用
CopyOnWriteArrayList
其中是ArrayList的线程安全变体,其中所有变异操作都是通过创建基础数组的新副本来实现的
我认为这取决于实现。您无法知道它是否在其他地方包装,因此不安全。谢谢,如果它在其他地方包装,您的意思是什么,您的意思是我无法知道它是否已锁定?这是否意味着我无法检查?仅仅因为您正在使用此方法进行同步,并不意味着其他代码无法修改它。如果你想在任何地方都安全,你应该用
list.synchronizedList()
来包装你的支持列表。看一看,了解更多信息。是的,我已经在做了。我假设我还需要同步排序块,因为它将在列表中迭代。如果您使用Java 8,我怀疑排序已经包括在内。是的,我使用的是synchronizedList,问题是我仍然需要手动同步在集合中迭代的任何内容。你有办法做到这一点吗?shmosel,你不能在java中传递整个对象。传递一个指向该对象的容器。这就是“指针”的定义。这就是为什么不能对函数参数进行同步。它是一个不同的对象容器,分配给函数调用的作用域(即在堆栈上)。@NickCardoso,Collections.sort()不会在集合上迭代。它将集合转储到数组中,对数组进行排序,然后将数组重置到集合中。如果您想确保在这三个步骤之间不插入或删除任何内容,那么只需将synchronized添加到排序方法中,这样在任何给定时间只有一个活动线程可以对任何集合进行排序。或者,我建议您创建自己的集合(可能是ConcurrentSortableCollection),在排序之前进行自身同步。这只有在没有并发集合适合您时才有用。是的,我知道指针是什么。我之前的陈述仍然有效。嗨,我的一个数据集被用来支持listview,我现在意识到这可能会改变整个问题。我尝试复制到CopyOnWriteArrayList之前
public class ListWrapper {
private final List<String> obj;
public ListWrapper (Object obj) {
this.obj = obj;
}
public List<String> getList() {
return obj;
}
}