从LinkedBlockingQueue中删除元素时,我的下面的代码线程安全吗?

从LinkedBlockingQueue中删除元素时,我的下面的代码线程安全吗?

问题描述:

我有一个下面的方法,它被多个线程同时调用来获取活动套接字。它需要LinkedBlockingQueue作为参数,然后我迭代,看看是否有liveSocket可用,如果它可用,然后我删除并返回该套接字。从LinkedBlockingQueue中删除元素时,我的下面的代码线程安全吗?

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

队列操作是线程安全的,但包含的对象的状态不是。如果'liveSocket'在另一个线程中处于非活动状态,而您在检查其状态并将其从队列中删除时会发生什么? –

队列操作操作是线程安全的,所以remove()不会抛出ConcurrentModificationException。但是,您在队列中包含的对象状态周围存在线程安全问题。

当您检查Holder对象的“活动”状态以及将其从队列中删除时,存在竞争状态。另一个线程可能同时在相同的代码中运行,可能的结果是两个线程都会采用同一个对象。无论哪个线程到达remove()最后会得到一个false回报,但是您不检查结果,所以你永远不会知道。两个线程都会尝试使用同一个对象。

您需要围绕搜索/删除操作进行同步。

出于好奇,这里是我用来证明ConcurrentModificationExceptionLinkedBlockingQueue发生代码:

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); 
    } 
} 

如果您LinkedBlockingQueue代替LinkedList你得到预期的ConcurrentModificationException

输出:

a 
b 
added 
c 
removed 
d 
f 
g 
x 

它不仅不是线程安全的,甚至在单个线程中也是错误的。您将在remove()上获得ConcurrentModificationException。您需要使用明确的Iterator并通过Iterator进行删除。

并且为了通过多线程的正确性,您需要在循环周围实现同步或信号量。

NB isEmpty()测试毫无意义。迭代已经必须检查。不要养狗,吠叫自己。

+0

实际上,删除工作得很好,并且没有与LinkedBlockingQueue(与普通的LinkedList对比)的CME。我很好奇,所以我尝试了它,java.util.concurrent集合是线程安全的,如Javadoc中所述。但是,队列中包含的对象存在线程安全问题。 –