Java 我怎样才能访问此列表,同时又是线程安全的?

Java 我怎样才能访问此列表,同时又是线程安全的?,java,multithreading,list,collections,Java,Multithreading,List,Collections,我的main生成了两个线程,它们都需要访问相同的列表。我不知道最好的方法是什么。这是我所拥有的,但我仍然遇到concurrentModificationException class Parent { private List<String> data; public List<String> getData() { return data; } public static void main(String args[]) {

我的main生成了两个线程,它们都需要访问相同的列表。我不知道最好的方法是什么。这是我所拥有的,但我仍然遇到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();
         }
      }
   } 
}
类父类{
私人名单数据;
公共列表getData(){
返回数据;
}
公共静态void main(字符串参数[]){
父级p=新父级();
p、 start();
}
公开作废开始(){
线程a=新线程(新线程a(此)).start();
线程b=新线程(新线程b(此)).start();
}
公共A实现可运行{
私人家长;
公共A(家长p){
父母=p;
}
公开募捐{
while(true){
parent.getData().add(“数据”);
}
}
}
公共B实现可运行{
私人家长;
公共B(家长p){
父母=p;
}
公开募捐{
迭代器i=parent.getData().Iterator();
while(i.hasNext()){
//和我一起做更多的事情
i、 删除();
}
}
} 
}

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

好吧,对于制作人/消费者,我推荐或。这将处理并发读写(在本例中为推送/轮询)

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

   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
            }
         }
      }
   }
请注意,对于阻塞队列的
轮询
结果没有空检查。这是因为,根据定义,阻塞队列会阻塞正在运行的线程,直到有东西存在

如果您希望消费者不与生产者发生冲突,那么您将需要定期轮询并休眠消费者线程。以下是使用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*/);
            }
         }
      }
   }

嗯,对于制作人/消费者,我推荐or。这将处理并发读写(在本例中为推送/轮询)

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

   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
            }
         }
      }
   }
请注意,对于阻塞队列的
轮询
结果没有空检查。这是因为,根据定义,阻塞队列会阻塞正在运行的线程,直到有东西存在

如果您希望消费者不与生产者发生冲突,那么您将需要定期轮询并休眠消费者线程。以下是使用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*/);
            }
         }
      }
   }

影响最小的更改可能是使用同步的setter。
这样,线程必须等待锁释放后才能添加到集合。

影响最小的更改可能是使用同步的setter。
这样,线程必须等待锁释放后才能添加到集合。

不要使用列表代替队列。软件包中有大量用于生产者/消费者操作的实用程序。您可能需要的是阻止队列,而不是列表。酷,我会研究这些。如果我换掉列表,理论上它的其余部分可以吗?不要用列表代替队列。软件包中有大量用于生产者/消费者操作的实用程序。您可能需要的是阻止队列,而不是列表。酷,我会研究这些。如果我换掉这个列表,理论上它的其余部分可以吗?不要使用Thread.sleep()来做这个。相反,通过设置volatile标志并中断使用者线程,或者在队列中放置“毒丸”,来关闭队列。否则,答案很好。请随意改进答案:)。“毒丸”方法肯定能很好地处理阻塞队列。ConcurrentLinkedQueue的优点是它在生产者和消费者之间没有争用,这是底层算法的一个非常酷的特性。因此线程轮询可能需要一些CPU,但生产者永远不会被消费者锁定。我将研究这个“毒丸”,我需要尽可能避免睡觉。我已经更新了第一个使用毒丸的示例。好的,我喜欢这个,它比我以前想的更简单、更好。Thx的帮助@ChillDon't使用Thread.sleep()进行此操作。相反,通过设置volatile标志并中断使用者线程,或者在队列中放置“毒丸”,来关闭队列。否则,答案很好。请随意改进答案:)。“毒丸”方法肯定能很好地处理阻塞队列。ConcurrentLinkedQueue的优点是它在生产者和消费者之间没有争用,这是底层算法的一个非常酷的特性。因此线程轮询可能需要一些CPU,但生产者永远不会被消费者锁定。我将研究这个“毒丸”,我需要尽可能避免睡觉。我已经更新了第一个使用毒丸的示例。好的,我喜欢这个,它比我以前想的更简单、更好。谢谢你的帮助@Chill