从LinkedBlockingQueue中删除元素时,我的下面的代码线程是否安全?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从LinkedBlockingQueue中删除元素时,我的下面的代码线程是否安全?相关的知识,希望对你有一定的参考价值。
我有一个下面的方法,由多个线程同时调用以获取实时套接字。它需要LinkedBlockingQueue
作为参数,然后我迭代,看看是否有任何可用的liveSocket,如果它可用,那么我删除并返回该套接字。
private Optional<Holder> getSocket(final LinkedBlockingQueue<Holder> endPoints) {
Optional<Holder> liveSocket = Optional.absent();
if (!endPoints.isEmpty()) {
for (Holder state : endPoints) {
// check if socket is live? if yes then remove and return that.
if (state.isLive()) {
liveSocket = Optional.of(state);
endPoints.remove(state);
return liveSocket;
}
}
}
return Optional.absent();
}
想检查我的上述代码是否是线程安全的?在这里,Holder
是一个不可变的类。
队列操作操作是线程安全的,因此remove()
不会抛出ConcurrentModificationException
。但是,您在队列中包含的对象的状态周围存在线程安全问题。
检查Holder
对象的“实时”状态和从队列中删除它之间存在竞争条件。另一个线程可能同时在相同的代码中运行,可能导致两个线程都采用相同的对象。无论哪个线程到达remove()
最后都会获得false
返回,但是你不会检查结果,所以你永远不会知道。然后两个线程都会尝试使用同一个对象。
您需要围绕搜索/删除操作进行同步。
为了好奇,这里是我用来表明ConcurrentModificationException
不会出现LinkedBlockingQueue
的代码:
public static void main(String[] args) throws Exception
{
String[] data = { "a", "b", "c", "d", "e", "f","g" };
LinkedBlockingQueue<String> lb = new LinkedBlockingQueue<>(Arrays.asList(data));
new Thread(() ->
{
try
{
Thread.sleep(2000);
lb.add("x");
System.out.println("added");
Thread.sleep(1000);
lb.remove("e");
System.out.println("removed");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}).start();
for (String s : lb)
{
System.out.println(s);
Thread.sleep(1000);
}
}
如果你用LinkedList
替换LinkedBlockingQueue
,你会得到预期的ConcurrentModificationException
。
输出:
a
b
added
c
removed
d
f
g
x
它不仅不是线程安全的,即使在单个线程中也是错误的。你会在ConcurrentModificationException
上得到一个remove()
。你需要使用一个明确的Iterator
并通过Iterator
进行删除。
并且为了通过多个线程获得正确性,您需要在循环周围进行同步或信号量。
NB isEmpty()
测试毫无意义。迭代已经必须检查它。不要养狗和自己吠叫。
以上是关于从LinkedBlockingQueue中删除元素时,我的下面的代码线程是否安全?的主要内容,如果未能解决你的问题,请参考以下文章
LinkedBlockingQueue与ArrayBlockingQueue
LinkedBlockingQueue 与ConcurrentLinkedQueue队列的不同与同
Java阻塞队列实现原理分析-ArrayBlockingQueue和LinkedBlockingQueue