java中附加到队列的多个线程
我有多个线程正在运行,需要附加到同一队列。这个队列被分割成多个变量,因此,实际上,我正在调用一个函数,该函数在每个变量的某个位置I追加。从中,我为每个方法添加了锁,如下所示。为数组中的100000个对象创建锁似乎并不有效。在java中,假设我有一个管理大量对象队列的对象。如何正确地同步addToQueue,而不牺牲同步方法的性能,只同步我附加到的floatQueue和intQueue中的位置java中附加到队列的多个线程,java,multithreading,locks,Java,Multithreading,Locks,我有多个线程正在运行,需要附加到同一队列。这个队列被分割成多个变量,因此,实际上,我正在调用一个函数,该函数在每个变量的某个位置I追加。从中,我为每个方法添加了锁,如下所示。为数组中的100000个对象创建锁似乎并不有效。在java中,假设我有一个管理大量对象队列的对象。如何正确地同步addToQueue,而不牺牲同步方法的性能,只同步我附加到的floatQueue和intQueue中的位置 class QueueManager { private float[] floatQueue;
class QueueManager {
private float[] floatQueue;
private int[] intQueue;
private int currentQueueSize;
private int maxQueueSize;
QueueManager(int sizeOfQueue) {
intQueue = new int[sizeOfQueue];
floatQueue = new float[sizeOfQueue];
currentQueueSize = 0;
maxQueueSize = sizeOfQueue;
}
public boolean addToQueue(int a, float b) {
if (currentQueueSize == maxQueueSize) {
return false;
}
else{
intQueue[currentQueueSize] = a;
floatQueue[currentQueueSize] = b;
currentQueueSize++;
return true;
}
}
}
考虑使用BlockingQueue(java.util.concurrent)使用比重新发明轮子更好。如果你这样做是为了练习,那么你可以使用两把锁,一把用来放东西,一把用来取东西。请参阅阻塞队列的来源
只需补充一点,获得正确的并发性是很棘手的,因此不要在部署应用程序中使用队列,而是依赖Java实现。要保持并行阵列“同步”,需要同步。这是保持对数组及其关联索引进行修改的原子性的唯一方法
而不是并行数组,您可以考虑一个不可变的元组对象,使用<代码>最终< /COD>字段,用于<代码> int <代码>和<代码>浮点,然后将这些添加到<代码>阻塞队列实现,如<代码> ArrayBlockingQueue < /代码> .<
如果存在大量搅动(频繁创建和丢弃对象),
synchronized
可能会比tuple执行得更好。您需要对它们进行分析才能确定。我发现的一个可能的解决方案是使用原子整数,它类似于锁定,但运行的级别比synchronized低得多。这是我的代码的更新副本,使用了一个AtomicInteger。请注意,它仍然需要测试,但在理论上,它应该足够了
import java.util.concurrent.atomic.AtomicInteger;
class ThreadSafeQueueManager {
private float[] floatQueue;
private int[] intQueue;
private AtomicInteger currentQueueSize;
private int maxQueueSize;
QueueManager(int sizeOfQueue) {
intQueue = new int[sizeOfQueue];
floatQueue = new float[sizeOfQueue];
currentQueueSize = new AtomicInteger(0);
maxQueueSize = sizeOfQueue;
}
public boolean addToQueue(int a, float b) {
if (currentQueueSize.get() == maxQueueSize) {
return false;
}
else{
try {
// Subtract one so that the 0th position can be used
int position = currentQueueSize.incrementAndGet() - 1;
intQueue[position] = a;
floatQueue[positions] = b;
return true;
}
catch (ArrayIndexOutOfBoundsException e) { return false;}
}
}
}
另外,作为参考,它值得一读
是一种非阻塞、无锁队列实现。它使用比较和交换指令而不是锁定。我想到的一种方法是向每个线程添加队列管理器的实例,并将变量更改为静态易失性数组。有没有更好的方法来实现这一点?听起来您想要的是使用offer方法的BlockingQueue?作为旁注,不幸的是,javaki中没有无锁队列实现。有没有一种方法可以锁定特定位置?如果是这样,我是否必须为每个位置创建一个锁?您需要在一个原子操作中更新位置和该位置中的内容,以使其线程安全。这显然需要某种类型的锁定-我认为没有任何方法可以在不使用本机实现的情况下提高阻塞队列实现的性能。我希望它是非阻塞的。我很抱歉这么说,但这是一个很好的例子,说明了为什么这类事情很难做到。假设代码经过充分修改,可以进行编译,那么在检查当前队列大小和将数据添加到队列之间,
addToQueue(int,float)
中仍然存在数据竞争。我不太确定这是否属实。这里也发现了类似的问题,但我只希望addToQueue的每个调用只占据一个位置,因为floatQueue和intQueue中的位置依赖于incrementAndGet(),为什么会出现问题?这些写入不需要是顺序的,唯一的要求是这些数组中的同一位置在每个工作负载中只写入一次。因此,如果我用try-catch包装else块,那么我是安全的?假设addToQueue(…)
中的初始测试更改为if(currentQueueSize.get()==maxQueueSize)
因此将编译代码,然后可能出现以下情况:1。线程进入方法并通过测试,但在执行int position=currentQueueSize.incrementAndGet()-1
之前,它被抢占。2.抢占线程也将通过测试,因为计数器尚未递增。此线程完成该方法。3.一段时间后,(1)中的原始线程被重新调度。如果数组现在已满,则会出现ArrayIndexOutOfBounds
。我认为这是一种解决方法。您还应该将初始条件从=
更改为=
。这也有点难看,因为在解决方案中,currentQueueSize
不一定具有当前队列大小。一个可能是实现一个方法int incrementAndGetWithLimit(AtomicInteger val,int limit)
,该方法只会将计数器增加一个点。您可以查看一下AtomicInteger#incrementAndGet()
,了解如何实现这一点的一些启示速度如何?在我支持最多四个并行线程的系统上,向同一队列中添加一百万个项目需要不到2.5秒的时间。如果您想自己进行测试,我这里提供了基准测试计划: