Java 这里哪些同步语句是不必要的? 首先是代码片段。。。 最终类加法器{ private final Set orders=Sets.newConcurrentHashSet(); 私有最终集ignoredItems=Sets.newConcurrentHashSet(); 私有布尔加法=假; 公共同步无效清除(){ 添加=错误; } 公共同步作废添加(订单){ 添加=订单。添加(订单); } 公共同步作废删除(订单){ 如果(增加)订单。删除(订单); } 公共同步作废禁令(字符串项){ 不知名人士。添加(项目); } 公共同步布尔has(顺序){ 退货订单。包含(订单); } 公共同步集getOrders(){ 退货订单; } 已忽略公共同步布尔值(字符串项){ 返回ignoredItems.contains(项); } } 添加的专用最终加法器=新加法器(); ... 布尔订阅; int i=10; 已同步(已添加){ 而(!(subscribed=client.getSubscribedOrders().containsAll(added.getOrders())&&(i>0)){ Helper.out(“…订单尚未订阅(尝试:%d)”,i); 睡眠(200); 我--; } } 我想知道的是。。。

Java 这里哪些同步语句是不必要的? 首先是代码片段。。。 最终类加法器{ private final Set orders=Sets.newConcurrentHashSet(); 私有最终集ignoredItems=Sets.newConcurrentHashSet(); 私有布尔加法=假; 公共同步无效清除(){ 添加=错误; } 公共同步作废添加(订单){ 添加=订单。添加(订单); } 公共同步作废删除(订单){ 如果(增加)订单。删除(订单); } 公共同步作废禁令(字符串项){ 不知名人士。添加(项目); } 公共同步布尔has(顺序){ 退货订单。包含(订单); } 公共同步集getOrders(){ 退货订单; } 已忽略公共同步布尔值(字符串项){ 返回ignoredItems.contains(项); } } 添加的专用最终加法器=新加法器(); ... 布尔订阅; int i=10; 已同步(已添加){ 而(!(subscribed=client.getSubscribedOrders().containsAll(added.getOrders())&&(i>0)){ Helper.out(“…订单尚未订阅(尝试:%d)”,i); 睡眠(200); 我--; } } 我想知道的是。。。,java,multithreading,synchronization,thread-safety,concurrenthashmap,Java,Multithreading,Synchronization,Thread Safety,Concurrenthashmap,有人能指出哪些同步的是不必要的吗 当然,这不是完整的代码,但是假设在完整的项目中调用所有方法,并且首先在检查值中调用一些方法组合,然后修改样式 添加的(类)由多个线程访问 client是外部服务器API的一部分,我还不能完全确定它是否是线程安全的,但我认为它必须是线程安全的 ConcurrentHashSet是一个google guava类,但它显然是基于ConcurrentHashMap的,文档称它具有所有相同的并发保证 但我并不完全理解这些保证是什么,即使我读了一些书。也就是说,我知道在同步

有人能指出哪些
同步的
是不必要的吗

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

添加的
(类)由多个
线程访问

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

ConcurrentHashSet
是一个
google guava类
,但它显然是基于
ConcurrentHashMap
的,文档称它具有所有相同的并发保证


但我并不完全理解这些保证是什么,即使我读了一些书。也就是说,我知道在同步的
HashMap
中检查并设置一个值(不使用
synchronized block
synchronized Map
上同步)是不好的,但是我不知道您是否可以在
ConcurrentHashMap
中这样做(不使用
同步块在
ConcurrentHashMap
上同步).

代码中真正需要同步的唯一情况是测试或更新添加的标志。您需要同步块以确保对标志的更改在线程之间可见,并且您还需要确保添加的标志更改与对orders数据结构的更改同步。synchronized关键字防止另一个线程在检查标志和更改数据结构之间闯入并执行某些操作(如果删除同步,则删除方法可能会像这样被破坏)

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

还请注意,由于您将引用传递给Orders集,因此此类之外的代码可以添加Orders。您应该做一些事情来保护此内部数据,例如将其包装在immutableSet中返回,以便调用方无法进行更改

一般来说,当您希望对更改施加一定的粒度时,会使用同步,其中您有两个或两个以上的更改要一起进行,而不可能交叉进行。例如,先检查后执行序列,其中您执行一些代码,根据其他内容的值进行更改,而您不希望其他线程不这样做o在检查和操作之间执行(这样可以做出操作的决定,然后允许该操作的条件发生变化,这样该操作可能无效)。如果单个值发生了变化,但它们是不相关的,那么您可以使它们不稳定或使用原子变量,并减少必须执行的锁定量

在clear方法这样的情况下,synchronized关键字可以被删除,这是一个有效的观点,在这种情况下,唯一需要更改的是添加的标志,它可能会变得不稳定。添加标志的目的一直让我难以理解。任何输入已经存在的值的内容都可能将标志变回false,这并不明显如果同时修改此结构,则基于标志当前值的任何操作都有意义


如果不知道确切的上下文,很难说,但一般来说,创建的类没有考虑它们与多个线程一起使用,可能需要在并发环境中使用之前进行大量的修改。

“added由多线程访问”然后,任何访问添加的
的方法都需要同步,所以前三个是必要的。啊,废话,我成了糟糕命名的受害者(至少是为了提问)…我指的是添加的添加程序,而不是添加的添加程序flag@ycomp:事实上,我不清楚添加的标志有什么好处。@NathanHughes你知道,你有一个观点。.看起来这是以前没有使用集合时的遗留物。现在我可以检查集合了。谢谢你指出这一点out@NathanHughes实际上它确实有一个目的,它只是隐藏在使用它的代码中(相当长),但您指出它是好的,因为它(添加的标志)不是线程安全的。这个特定的类和相关的代码最初是在没有考虑ab的情况下创建的
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--;
        }
    }