Java 编写一个包含整数的ArrayList,这些整数将被并发访问

Java 编写一个包含整数的ArrayList,这些整数将被并发访问,java,multithreading,concurrency,synchronization,atomic,Java,Multithreading,Concurrency,Synchronization,Atomic,要求是,我需要编写一个整数数组列表。我需要不同整数的线程安全访问(写入、读取、增加、减少),还需要允许最大并发性 每个整数的运算也很特殊,如下所示: 最常见的操作是阅读 其次,频繁操作仅当值大于零时才减少1。或者,增加一(无条件) 添加/删除元素很少,但仍然需要 我想到了原子整数。然而,这变得不可用,因为我想要的原子操作是比较,如果不是零,则减少。但是,AtomicInteger提供的原子操作是compare if equal和set。如果您知道如何在这种情况下应用AtomicInteger,请

要求是,我需要编写一个整数数组列表。我需要不同整数的线程安全访问(写入、读取、增加、减少),还需要允许最大并发性

每个整数的运算也很特殊,如下所示:

  • 最常见的操作是阅读
  • 其次,频繁操作仅当值大于零时才减少1。或者,增加一(无条件)
  • 添加/删除元素很少,但仍然需要
  • 我想到了原子整数。然而,这变得不可用,因为我想要的原子操作是比较,如果不是零,则减少。但是,AtomicInteger提供的原子操作是compare if equal和set。如果您知道如何在这种情况下应用AtomicInteger,请在此处提出

    我所想的是同步对每个整数的访问,如下所示:

    ArrayList <Integer> list;
     ... ...
    // Compare if greater than zero, and decrease
    MutableInt n = list.get(index);
    boolean success = false;
    synchronized (n) {
        if (n.intValue()>0) { n.decrement(); success=true; }
    }
    
    // To add one
    MutableInt n = list.get(index);
    synchronized (n) {
        n.increment();
    }
    
    // To just read, I am thinking no need synchronization at all.
    int n = list.get(index).intValue();
    
    ArrayList列表;
    ... ...
    //如果大于零,则进行比较,然后减小
    MutableInt n=list.get(索引);
    布尔成功=假;
    已同步(n){
    如果(n.intValue()>0){n.decrement();success=true;}
    }
    //加一
    MutableInt n=list.get(索引);
    已同步(n){
    n、 增量();
    }
    //只是为了阅读,我认为根本不需要同步。
    int n=list.get(index.intValue();
    
    我的解决方案有副作用吗?维护数百甚至数千个同步整数是否有效

    更新:我还认为允许对每个元素进行并发访问既不实际也不有益,因为实际的并发访问受到处理器数量的限制。也许我只是使用几个同步对象来保护列表的不同部分,那么就足够了吗


    另外一件事是实现add/delete操作,它是线程安全的,但不会影响其他操作的并发性。我认为ReadWriteLock,对于添加/删除,需要获取写锁,对于其他操作(更改一个整数的值),需要获取读锁。这是一种正确的方法吗?

    我认为使用读锁访问列表和写锁添加/删除列表是正确的

    对于以下值,您仍然可以使用
    AtomicInteger

    // Increase value
    value.incrementAndGet()
    
    // Decrease value, lower bound is 0
    do {
        int num = value.get();
        if (num == 0)
            break;
    } while (! value.compareAndSet(num, num - 1)); // try again if concurrently updated
    

    我认为,如果您可以使用固定大小的列表,那么使用单个
    AtomicIntegerArray
    比使用多个
    AtomicInteger
    s更好:

    public class AtomicIntList extends AbstractList<Integer> {
    
      private final AtomicIntegerArray array;
    
      public AtomicIntList(int size) {
        array=new AtomicIntegerArray(size);
      }
      public int size() {
        return array.length();
      }
      public Integer get(int index) {
        return array.get(index);
      }
      // for code accessing this class directly rather than using the List interface
      public int getAsInt(int index) {
        return array.get(index);
      }
      public Integer set(int index, Integer element) {
        return array.getAndSet(index, element);
      }
      // for code accessing this class directly rather than using the List interface
      public int setAsInt(int index, int element) {
        return array.getAndSet(index, element);
      }
      public boolean decrementIfPositive(int index) {
        for(;;) {
          int old=array.get(index);
          if(old<=0) return false;
          if(array.compareAndSet(index, old, old-1)) return true;
        }
      }
      public int incrementAndGet(int index) {
        return array.incrementAndGet(index);
      }
    }
    
    公共类AtomicIntList扩展了AbstractList{
    私有最终原子整数数组;
    公共原子整数列表(整数大小){
    数组=新的原子整数数组(大小);
    }
    公共整数大小(){
    返回array.length();
    }
    公共整数get(整数索引){
    返回数组.get(索引);
    }
    //用于直接访问此类而不是使用列表接口的代码
    公共整数getAsInt(整数索引){
    返回数组.get(索引);
    }
    公共整数集(整数索引,整数元素){
    返回数组.getAndSet(索引,元素);
    }
    //用于直接访问此类而不是使用列表接口的代码
    公共int setAsInt(int索引,int元素){
    返回数组.getAndSet(索引,元素);
    }
    公共布尔递减正数(整数索引){
    对于(;;){
    int old=array.get(索引);
    
    如果(旧作为这个问题的更新…我发现最简单的解决方案,就是同步所有可能的冲突方法的整个代码块,即使从性能的角度来看,结果也是最好的。同步代码块解决了这两个问题-访问每个计数器,还向计数器列表添加/删除元素

    这是因为即使只应用了读锁,ReentrantReadWriteLock的开销也非常高。与读/写锁的开销相比,操作本身的成本非常小,任何额外的锁定都不值得这么做


    应高度注意ReentrantReadWriteLock API文档中的声明:“ReentrantReadWriteLocks…通常只有在…需要的操作开销超过同步开销时才有价值”.

    n++
    n--
    实际上不会更改
    列表中的值。更合理的做法是将列表用作锁,而不是列表的元素。它必须是
    ArrayList
    还是任何列表都可以?如果允许同时添加/删除元素,您如何知道要更新哪个元素?索引将不起作用,bec因为删除可能会在调用方提供的索引和方法访问元素之间移动索引。我想您在这个线程中找到了答案:[1]:是的,但请记住,读锁必须围绕确定要更新的索引的代码,不管这是什么奇怪的逻辑。我的意思是,如果列表有数千个整数,代码如何知道(例如,索引847处的整数)是要更新的?在代码开始确定要更新的索引之前,必须建立读锁,并在更新完成之前保持读取锁定。谢谢。开始时我觉得它有点关联,但我认为它可以工作。实际上,这是以与AtomicInteger的另一个操作类似的方式写入的…看起来compareAndSet是一个神奇的原子操作。比较同步obj的选项ect,以及您所写的带有AtomicInteger和重试机制的,我们如何比较性能?您是指在最坏情况下的性能还是在正常使用情况下的性能?在正常使用情况下,我希望这会更好,因为它不会浪费时间建立同步锁。在具有极高争用的最坏情况下对于单个整数(如果您有数千个整数,则不太可能),此代码将在重试循环中使用更多的CPU,但这是否比同步等待更糟糕尚不得而知。要比较性能,您只需将其置于“正常”负载下。请保持最小值