从LinkedBlockingQueue中删除元素时,我的代码线程是否安全?

时间:2017-12-18 03:45:34

标签: java multithreading concurrency thread-safety

我有一个下面的方法,由多个线程同时调用以获取实时套接字。它需要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是一个不可变的类。

2 个答案:

答案 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()测试毫无意义。迭代已经必须检查它。不要养狗和自己吠叫。