Java并发-使用哪种技术来实现安全性?

Java并发-使用哪种技术来实现安全性?,java,concurrency,thread-safety,Java,Concurrency,Thread Safety,我有个人的名单。有两个API调用来更新它(添加和删除): 我知道可能存在并发问题。我阅读了3种解决方案: synchronized方法 使用集合。同步列表() CopyOnWriteArrayList 我不确定该选择哪一个来避免这种不一致 1) 同步方法 2) 使用Collections.synchronizedlist 3) CopyOnWriteArrayList 一切都会成功,这取决于你需要什么样的性能/功能 方法#1和#2是阻塞方法。如果同步这些方法,则需要自己处理并发性。如果你把一个列

我有个人的名单。有两个API调用来更新它(添加和删除):

我知道可能存在并发问题。我阅读了3种解决方案:

  • synchronized
    方法
  • 使用集合。同步列表()
  • CopyOnWriteArrayList
  • 我不确定该选择哪一个来避免这种不一致

    1) 同步方法

    2) 使用Collections.synchronizedlist

    3) CopyOnWriteArrayList

    一切都会成功,这取决于你需要什么样的性能/功能

    方法#1和#2是阻塞方法。如果同步这些方法,则需要自己处理并发性。如果你把一个列表包装起来,它会为你处理它。(IMHO#2更安全——只需确保按照文档中的说明使用它,并且不要让任何东西访问包装在synchronizedList中的原始列表。)

    是在某些应用程序中使用的一种奇怪的东西。它是一个非阻塞的准不可变列表,也就是说,如果线程a在线程B更改列表时遍历该列表,那么线程a将遍历旧列表的快照。如果您需要非阻塞性能,并且您很少向列表中写入数据,而是经常从列表中读取数据,那么这可能是最好的使用方法


    编辑:至少还有两个其他选项:

    4) 使用而不是
    ArrayList
    <代码>向量实现了
    列表
    并且已经同步。然而,人们普遍不赞成它,因为它被认为是一门古老的课程(从Java1.0开始就存在了!),应该等同于#2

    5) 仅从一个线程串行访问
    列表
    。如果您这样做,就可以保证
    列表本身不会出现任何并发问题。一种方法是逐个使用任务并将其排队以访问列表。这会将资源争用从列表移动到
    执行器服务
    ;如果任务很短,则可以,但如果某些任务很长,则可能会导致其他任务阻塞的时间超出预期

    最后,您需要考虑应用程序级别的并发性:线程安全应该是一项要求,并了解如何通过尽可能简单的设计获得所需的性能


    另一方面,您在add()和delete()中调用了
    personNameIdMap.get(newPersonName)
    两次。如果另一个线程在每个方法中的两个调用之间修改personNameIdMap,则会出现并发问题。你最好去做

    PersonId id = personNameIdMap.get(newPersonName);
    if (id != null){
        myPersonId.add(id);
    } 
    else 
    {
        // something else
    }
    
  • 同步是处理线程的旧方法。避免使用java.util.concurrent包中主要表达的新习惯用法
  • 见1
  • CopyOnWriteArrayList具有快速读取和慢速写入。如果您对它做了很多更改,它可能会开始拖累您的性能

  • 并发性不是关于在单个方法中使用何种机制或类型的孤立选择。您需要从更高的层次来考虑它,以了解它的所有影响。

    集合。synchronizedList
    是最容易使用的,可能是最好的选择。它只是用
    synchronized
    包装基础列表。请注意,多步骤操作(例如for loop)仍然需要由您进行同步

    一些快速的事情

    • 除非您真的需要,否则不要同步该方法-它只是锁定整个对象,直到该方法完成;几乎没有理想的效果
    • CopyOnWriteArrayList是一个非常专门的列表,由于您有一个add方法,所以很可能不需要它。它本质上是一个普通的
      ArrayList
      ,但每次添加一些内容时,整个阵列都会重建,这是一项非常昂贵的任务。它是线程安全的,但不是真正想要的结果

    从您发布的代码来看,这三种方式都是可以接受的。但是,有一些特定的特征:

    #3:这应该具有与#2相同的效果,但根据系统和工作负载的不同,可能运行得更快或更慢


    #1:这种方式最灵活。只有使用#1才能使add()和delete()方法更加复杂。例如,如果您需要读取或写入列表中的多个项目,那么您不能使用#2或#3,因为其他一些线程仍然可以看到列表被半更新。

    您是否在这些方法中对
    personNameIdMap
    进行更改,或者对任何其他数据结构的访问也应该同步?如果是这样,则最容易将这些方法标记为已同步;否则,您可能会考虑使用<代码>集合。SimuleDistList获得 MyHuMIDID<代码>的同步视图,然后通过该同步视图执行所有列表操作。请注意,在这种情况下,您不应直接操作
    myPersonId
    ,而应仅通过从
    Collections.synchronizedList
    调用返回的列表执行所有访问

    无论采用哪种方式,都必须确保不会出现对同一个非同步数据结构同时进行读写或两次写操作的情况。记录为线程安全的数据结构或从
    集合返回的数据结构。synchronizedList
    集合。synchronizedMap
    等都是此规则的例外,因此对这些数据结构的调用可以放在任何地方。但是,非同步数据结构仍然可以在声明为同步的方法中安全使用,因为JVM保证这些方法永远不会同时运行,因此不可能存在并发读/写操作。

    Java并发(多线程):

    并发性是并行运行多个程序或程序的多个部分的能力。如果一个耗时的任务可以异步或并行执行,这将提高程序的吞吐量和交互性

    我们可以用Java进行并发编程。
    PersonId id = personNameIdMap.get(newPersonName);
    if (id != null){
        myPersonId.add(id);
    } 
    else 
    {
        // something else
    }