Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/amazon-web-services/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何确保传递给方法的集合的同步?_Java_Android_Multithreading - Fatal编程技术网

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

Java 如何确保传递给方法的集合的同步?,java,android,multithreading,Java,Android,Multithreading,我在一个内部类上有以下方法,我使用该方法对多线程应用程序中的多个集合进行排序。我想确保在排序时同步每个集合(我已经在迭代的其他地方同步了) 注意:其中一个集合支持ListView的适配器 private void排序(列表集合){ if(集合!=null){ 集合。排序(集合,…); } } 问题是如果我包装synchronized(collection){Collections.sort}IDE会警告我在方法参数上进行同步是不安全的 我知道这是一个新的参考,但参考仍然指向原始收藏,所以这是安

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

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

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; 
    } 
}