如何访问此列表并且还是线程安全的?

时间:2016-06-08 21:57:12

标签: java multithreading list collections

我的主要产生2个线程,他们都需要访问相同的列表。我不确定最好的办法是什么。这是我的,但我仍然遇到concurrentModificationException。

class Parent {
   private List<String> data;

   public List<String> getData() {
      return data;
   }

   public static void main(String args[]) {
      Parent p = new Parent();
      p.start();
   }

   public void start() {
      Thread a = new Thread(new A(this)).start();
      Thread b = new Thread(new B(this)).start();
   }

   public A implements Runnable {
      private Parent parent;

      public A(Parent p) {
         parent = p;
      }

      public void run() {
         while (true) {
            parent.getData().add("data");
         }
      }
   }

   public B implements Runnable {
      private Parent parent;

      public B(Parent p) {
         parent = p;
      }

      public void run() {
         Iterator<String> i = parent.getData().iterator();
         while(i.hasNext()) {
            // do more stuff with i
            i.remove();
         }
      }
   } 
}

我的A类基本上是数据的生成者,B是消费者。我接受这种错误方式的可能性。所以欢迎所有的帮助。我只需要能够安全地从一个线程添加到列表中,并从另一个线程中删除列表中的项目。提前谢谢。

2 个答案:

答案 0 :(得分:4)

嗯,对于制作人/消费者,我建议LinkedBlockingQueueConcurrentLinkedQueue。这将处理并发读取和写入(或在这种情况下推送/轮询)。

您可能希望您的消费者运行,直到向其发送一些关闭条件。如果您使用阻塞队列,这意味着您将要发送一个排队的项目,指示消费者应该停止消费。这将是一个关闭的阻塞队列实现。

   public enum QueueItemType {
      CONSUMABLE,
      SHUTDOWN
   }

   public class QueueItem {
      public final QueueItemType type;
      public final String payload;

      public QueueItem(QueueItemType type, String payload) {
         this.type = type;
         this.payload = payload;
      }
   }

   public class B implements Runnable {
      private Parent parent;

      public B(Parent p) {
         parent = p;
      }

      public void run() {
         while(true) {
            QueueItem data = parent.getData().poll();
            if (data.type == QueueItemType.SHUTDOWN) {
               break;
            } else {
               // do more stuff with data.payload
            }
         }
      }
   }

请注意,阻塞队列的poll结果没有空检查。这是因为,根据定义,阻塞队列阻塞正在运行的线程,直到存在某些东西。

如果您希望消费者不与生产者竞争,那么您需要定期轮询并睡眠消费者线程。以下是使用ConcurrentLinkedQueue的示例:

   public class B implements Runnable {
      private Parent parent;

      public B(Parent p) {
         parent = p;
      }

      public void run() {
         while(parent.isStillRunning()) {
            String data = parent.getData().poll();
            if (data != null) {
              // do more stuff with data
            } else {
              Thread.sleep(10 /*10 ms, but you can make this whatever poll interval you want*/);
            }
         }
      }
   }

答案 1 :(得分:0)

影响最小的变化可能是使用同步的setter。 这样一个线程就必须等待锁释放才能添加到Collection。