这里不需要哪些同步语句?

时间:2015-10-20 20:05:30

标签: java multithreading synchronization thread-safety concurrenthashmap

首先是代码片段......

final class AddedOrders {
    private final Set<Order> orders = Sets.newConcurrentHashSet();
    private final Set<String> ignoredItems = Sets.newConcurrentHashSet(); 
    private boolean added = false;

    public synchronized void clear() {
        added = false;
    }

    public synchronized void add(Order order) {
        added = orders.add(order);
    }

    public synchronized void remove(Order order) {
        if (added) orders.remove(order); 
    }

    public synchronized void ban(String item) {
        ignoredItems.add(item);
    }

    public synchronized boolean has(Order order) {
        return orders.contains(order);
    }

    public synchronized Set<Order> getOrders() {
        return orders;
    }

    public synchronized boolean ignored(String item) {
        return ignoredItems.contains(item);
    }
}

private final AddedOrders added = new AddedOrders();


...
    boolean subscribed;
    int i = 10;
    synchronized (added) {
        while (!(subscribed = client.getSubscribedOrders().containsAll(added.getOrders())) && (i>0)) {
           Helper.out("...order not subscribed yet (try: %d)", i);
           Thread.sleep(200);
           i--;
        }
    }

我想知道的......

有人可以指出哪些synchronized不是必需的吗?

当然这不是完整的代码,但假设在整个项目中调用所有方法,并且首先在 检查值中调用某些方法组合,然后修改 风格

added(该类)由多个Thread s

访问

client是外部服务器API的一部分,我不完全确定它是否是线程安全但我认为它必须是

ConcurrentHashSet是一个google guava Class,但显然基于ConcurrentHashMap,并且文档说它带有所有相同的并发保证。

但即使我做了一些阅读,我也完全不明白这些保证是什么。即我知道在同步HashMap中检查和设置值是不行的(不使用synchronized Mapsynchronized block上同步),但我不知道你是否可以这样做在ConcurrentHashMap或不在ConcurrentHashMap中使用synchronized block同步。

1 个答案:

答案 0 :(得分:2)

您的代码中您真正需要同步的唯一情况是您测试或更新添加的标志的情况。您需要synchronized块以确保跨线程可以看到对标志的更改,并且还需要确保添加的标志更改是在更改订单数据结构的同时进行的。 synchronized关键字使另一个线程无法插入并在检查标志和更改数据结构之间执行某些操作(如果删除同步,则remove方法可能会被破坏)。

到最后的代码似乎有问题,因为你已经锁定了添加的对象,然后没有放开锁,任何其他线程都没有机会进行线程所做的更改寻找。虽然看起来你正在等待另一个对象改变,但这种批评可能是无效的。但是,持有锁睡觉似乎很危险。这就是为什么Object#wait释放它获得的锁。

另请注意,由于您将引用传递给订单集,因此此类外的代码可以添加订单。您应该采取措施保护内部数据,例如将其包装在immutableSet中,以便调用者无法进行更改。

一般情况下,如果要对更改施加一些粒度,则会使用同步,其中您需要进行两项或更多更改,而不会进行交错。一个示例是一个check-then-act序列,您可以执行一些代码,根据其他内容的值进行更改,并且您不希望在检查和操作之间执行其他一些线程(所以可以做出行动决定,然后允许该行动改变的条件,以便该行动可能无效)。如果单个值被更改但它们不相关,那么您可以使它们变为volatile或使用原子变量,并减少您必须执行的锁定量。

在clear方法中可以删除synchronized关键字的有效点,其中唯一改变的是添加的标志,可以使其变为易失性。添加标志的目的继续无法实现。任何输入已经存在的值的东西都可以将标志重新变为假,如果这个结构被修改,根据标志的当前值有什么意义推理任何动作并不明显同时进行。

在不知道确切的上下文的情况下很难说,但一般而言,在不考虑将其用于多个线程的情况下创建的类可能需要在并发环境中使用之前进行大量的重新设计。