Java 这段代码是线程安全的吗?有更好的实现方法吗?

Java 这段代码是线程安全的吗?有更好的实现方法吗?,java,multithreading,Java,Multithreading,我有以下代码: public class MySystem { private ConcurrentHashMap<Integer, Integer> disabledList = new ConcurrentHashMap<>(); public void toggle(int id) { synchronized (disabledList) { if (disabledList.containsKey(id)

我有以下代码:

public class MySystem {
    private ConcurrentHashMap<Integer, Integer> disabledList = new ConcurrentHashMap<>();

    public void toggle(int id) {
        synchronized (disabledList) {
            if (disabledList.containsKey(id)) {
                disabledList.remove(id);
            } else {
                disabledList.put(id);
            }
        }
    }

    public boolean isInactive(int id) {
      return disabledList.containsKey(id);
    }

}
公共类MySystem{
私有ConcurrentHashMap disabledList=新ConcurrentHashMap();
公共无效切换(int-id){
已同步(禁用列表){
if(禁用列表容器(id)){
禁用列表。删除(id);
}否则{
禁用列表放置(id);
}
}
}
公共布尔值isInactive(整数id){
返回disabledList.containsKey(id);
}
}
多个线程可能会调用
toggleId
方法。上述代码是线程安全的吗?是否有更好的数据结构可以使用,如
CopyOnWriteArrayList
ConcurrentHashMap


理想情况下,我希望摆脱同步块,但我不确定这是否可行。

这里的问题是Java中的并发类不会锁定自己以实现线程安全。因此,您不能依赖代码中某个位置的
synchronized
来与对该对象的其余访问同步

从Java文档中引用
ConcurrentHashMap

但是,即使所有操作都是线程安全的,检索操作也不需要锁定,并且不支持以阻止所有访问的方式锁定整个表。该类在依赖于其线程安全性而不依赖于其同步细节的程序中与哈希表完全互操作

CopyOnWriteArrayList
的Java文档没有指定其锁定特性,但由于它是写时复制的,我怀疑它是否在内部进行了锁定。当然,我不会在生产代码中使用它。

您的
toggleId()
在隔离状态下是线程安全的

我的意思是,如果奇数个线程同时为相同的
I
调用
toggleId(I)
,那么最终结果将是
I
被切换,如果偶数同时调用它,那么最终结果将是no-op

但是它与
isInactive()
的关系呢?如果线程A调用
isInactive(i)
,然后线程B在线程A能够对
isInactive(i)
返回的值执行操作之前调用
toggleId(i)
,这意味着什么


我想去掉同步块,但我不确定这是否可行


一句话,不可能。只有
ConcurrentHashMap
为您实现了线程安全的
toggleId
-类函数,才有可能实现,但我在它的API中看不到任何适合的东西<代码>禁用列表。putIfAbsent(…)完成了一半的工作,但没有
putIfAbsentOrDeleteIfPresent(…)
方法。

您的代码使用的是哪个
ConcurrentHashSet
实现?呃。。。调用类
System
有点棘手,因为它会影响
java.lang.System
类。但是我需要在同步化块中检查hashset是否包含id和删除id的代码,否则当另一个线程(比如t1)运行时可能会出现争用条件在线程t2检查包含后调用remove?是否有其他代码使用
disabledList
?我要声明
disabledList
a
final
。另外,这是
列表
,还是
地图
?如果是后者,是否要执行
.put(键、值)
?你看过CHM上的
putIfAbsent
contract了吗?你是说isInactive方法也需要标记为synchronized?是的,没错。这也会破坏映射中的并发性。此时,您还可以使用一个普通的旧同步映射。