如何同步访问其他类的成员?

时间:2014-02-27 22:36:13

标签: java multithreading thread-safety synchronized

我正在试图弄清楚如何将读/写访问权限与来自不同类的同步列表同步。

一个小例子:我在一个班级ListProvider中有一个同步列表,我在另一个班级Filter中访问它。顾名思义,此类基于(in)验证检查isInvalid执行一些过滤。

filter方法首先获取列表引用,然后收集要在临时列表中删除的条目,以便不会遇到并发修改问题,最后从列表中删除条目:

public class Filter {

    ListProvider listProvider;

    ...

    public void filter() {
        List<String> listProviderList = listProvider.getList();

        List<String> entriesToRemove = new ArrayList<>();

        // collect
        for (String entry : listProviderList)
            if (isInvalid(entry)) {
                entriesToRemove.add(entry);
            }
        }

        // remove
        for (String entry : entriesToRemove) {
            listProviderList.remove(entry);
        }       
    }
}

我的问题:如何在filter读取和写入时确保没有其他线程修改列表?

如果它是Filter自己的列表,我只会这样做:

synchronized(myList) {
    // collect
    // remove
}

但在这种情况下,我不确定将什么用作监视器。

4 个答案:

答案 0 :(得分:2)

  

但在这种情况下,我不确定将什么用作监视器。

要为特定任务创建监视器,使用private final Object是一种很好的模式:

 private final Object listUpdateLock = new Object();
 ...
 synchronized(listUpdateLock) {
    ...
 }

确保ListProviderprivate以及对列表的所有访问都在synchronized块内完成非常重要 - 即使只是从中读取。

在这种情况下,您正在更新列表,您可以创建一个临时列表,然后在完成后替换它。我不确定你能用ListProvider做到这一点。然后你就可以制作清单volatile

答案 1 :(得分:2)

这似乎应该使用lock。锁定就像是同步的,但它更灵活一些。它不需要周围的块,它有一些扩展功能。还有一些不同类型的锁。 ReentrantLock非常类似于同步。

public class ListProvider<E> {
    private final List<E> theList = new ArrayList<E>();
    private final ReentrantLock listLock = new ReentrantLock();

    public final List<E> lockList() {
        listLock.lock();
        return theList;
    }

    public final void unlockList() {
        listLock.unlock();
    }
}

/* somewhere else */ {

    List<E> theList = listProvider.lockList();

    /*
     * perform
     * multiple
     * operations
     *
     */

    listProvider.unlockList();
}

this和synchronized之间的主要区别是:

  • 隐藏了实际的锁定机制。这有利于抽象;然而,
  • 客户端必须记住显式解锁,而同步监视器出口处于块分隔符。

有一个名为ReentrantReadWriteLock的锁,您可能会发现它很有用,因为多个线程可以同时读取。 ReadWriteLock解释了它的工作原理。

答案 2 :(得分:1)

不要遍历原始列表,而是创建它的副本以查找无效元素。完成过滤后,您可以安全地从原始列表中删除无效元素:

public class Filter {

    ListProvider listProvider;

    ...

    public void filter() {
        List<String> listProviderCopy = new ArrayList<>(listProvider.getList());

        List<String> entriesToRemove = new ArrayList<>();

        // collect
        for (String entry : listProviderCopy)
            if (isInvalid(entry)) {
                entriesToRemove.add(entry);
            }
        }

        listProvider.getList().removeAll(entriesToRemove);
    }
}

答案 3 :(得分:0)

您可能想要使用SynchronizedList

List<String> list = new ArrayList<>();

List<String> synch = Collections.synchronizedList(list);

more