我有一个下面的方法,由多个线程同时调用以获取实时套接字。它需要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
是一个不可变的类。
答案 0 :(得分:1)
队列操作操作是线程安全的,因此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
答案 1 :(得分:-1)
它不仅不是线程安全的,甚至在单个线程中也是错误的。您将在ConcurrentModificationException
上获得remove()
。您需要使用明确的Iterator
并通过Iterator
进行删除。
为了通过多个线程获得正确性,您需要在循环周围进行同步或信号量。
注意isEmpty()
测试毫无意义。迭代已经必须检查它。不要养狗和自己吠叫。