Java 编写一个包含整数的ArrayList,这些整数将被并发访问
要求是,我需要编写一个整数数组列表。我需要不同整数的线程安全访问(写入、读取、增加、减少),还需要允许最大并发性 每个整数的运算也很特殊,如下所示:Java 编写一个包含整数的ArrayList,这些整数将被并发访问,java,multithreading,concurrency,synchronization,atomic,Java,Multithreading,Concurrency,Synchronization,Atomic,要求是,我需要编写一个整数数组列表。我需要不同整数的线程安全访问(写入、读取、增加、减少),还需要允许最大并发性 每个整数的运算也很特殊,如下所示: 最常见的操作是阅读 其次,频繁操作仅当值大于零时才减少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,但这是否比同步等待更糟糕尚不得而知。要比较性能,您只需将其置于“正常”负载下。请保持最小值