编写一个带有整数的ArrayList,它们将被同时访问

时间:2015-10-04 03:43:45

标签: java multithreading concurrency synchronization atomic

要求是,我需要编写一个整数的ArrayList。我需要不同整数的线程安全访问(写,读,增,减),还需要允许最大的并发。

每个整数的操作也很特殊,如下所示:

  1. 最常见的操作是阅读
  2. 其次,只有当值大于零时,频繁操作才会减1。或者,增加一个(无条件地)
  3. 添加/删除元素很少见,但仍然需要。
  4. 我想到了AtomicInteger。然而,这变得不可用,因为我想要的原子操作是比较,如果不是零,然后减少。然而,AtomicInteger提供的原子操作,如果相等则进行比较,并设置。如果您知道如何应用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();
    

    根据我的解决方案,有任何副作用吗?维护数百甚至数千个同步整数是否有效?

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

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

2 个答案:

答案 0 :(得分:1)

我认为您使用读锁来访问列表并在列表中添加/删除锁定是正确的。

您仍然可以使用$ valgrind ./bin/struct_book_simple <dat/10int_nl.txt ==9039== Memcheck, a memory error detector ==9039== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==9039== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==9039== Command: ./bin/struct_book_simple ==9039== Book[ 0] : 8572 <snip> Book[ 9] : 1495 ==9039== ==9039== HEAP SUMMARY: ==9039== in use at exit: 0 bytes in 0 blocks ==9039== total heap usage: 12 allocs, 12 frees, 272 bytes allocated ==9039== ==9039== All heap blocks were freed -- no leaks are possible ==9039== ==9039== For counts of detected and suppressed errors, rerun with: -v ==9039== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 作为值:

AtomicInteger

答案 1 :(得分:1)

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

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);
  }
}

直接访问此类而非通过List<Integer>接口访问此类可能会使用方法getAsIntsetAsInt来避免装箱转换。这是一种常见的模式。由于方法decrementIfPositiveincrementAndGet不是List接口的一部分,因此它们始终使用int值。