Java 即使使用同步块,ArrayList也不是线程安全的?

Java 即使使用同步块,ArrayList也不是线程安全的?,java,multithreading,collections,Java,Multithreading,Collections,我尝试使用ArrayList解决生产者和消费者的问题(我知道ArrayList不是线程安全的),我确保使用synchronized关键字放置列表,但仍然进入ConcurrentModificationException。这就是错误所在 起始制作人请提供作业详细信息:TestJob作业 已完成:线程“使用者”中的TestJob异常 位于的java.util.ConcurrentModificationException java.util.ArrayList$Itr.checkForComodif

我尝试使用ArrayList解决生产者和消费者的问题(我知道ArrayList不是线程安全的),我确保使用
synchronized
关键字放置列表,但仍然进入
ConcurrentModificationException
。这就是错误所在

起始制作人请提供作业详细信息:TestJob作业 已完成:线程“使用者”中的TestJob异常 位于的java.util.ConcurrentModificationException java.util.ArrayList$Itr.checkForComodification(未知源代码)位于 java.util.ArrayList$Itr.next(未知源代码)位于 test.thread.Consumer.run(Consumer.java:25)位于 java.lang.Thread.run(未知源)

这是我的密码:

    package test.thread;

import java.util.List;
import java.util.Scanner;

public class Producer implements Runnable {

    private List<String> jobs = null;

    public Producer(List<String> jobs) {
        this.jobs = jobs;
    }

    @Override
    public void run() {

        System.out.println("Starting Producer");
        while(true)
        {
            synchronized (jobs) {
                try {
                if (jobs.isEmpty()) {


                        System.out.println("Please provide the job details: ");
                        Scanner scanner = new Scanner(System.in);
                        String job = scanner.nextLine();
                        jobs.add(job);
                        scanner.close();
                        jobs.notifyAll();
                        Thread.sleep(4000);

                } else {
                    jobs.wait();
                }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }

    }

}


package test.thread;

import java.util.List;

public class Consumer implements Runnable {

    private List<String> jobs = null;

    public Consumer(List<String> list) {
        this.jobs = list;
    }

    @Override
    public void run() {

        while(true)
        {
            synchronized (jobs) {
                try {
                    if (jobs.isEmpty()) {

                        jobs.wait();

                    } else {
                        for (String job : jobs) {
                            System.out.println("Job completed: " + job);
                            jobs.remove(job);
                            Thread.sleep(2000);
                        }

                        jobs.notifyAll();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

        }

    }

}



package test.thread;

import java.util.ArrayList;
import java.util.List;

public class TestThread {

    public static void main(String...strings )
    {

        List<String> list = new ArrayList<String>();
        Producer producer = new Producer(list);
        Consumer consumer = new Consumer(list);
        Thread prodThread = new Thread(producer, "producer");
        Thread consThread = new Thread(consumer, "consumer");
        prodThread.start();
        consThread.start();
    }




}
package test.thread;
导入java.util.List;
导入java.util.Scanner;
公共类生产者实现Runnable{
私有列表作业=null;
公共制片人(列出职位){
这个.工作=工作;
}
@凌驾
公开募捐{
System.out.println(“起始生产者”);
while(true)
{
已同步(作业){
试一试{
if(jobs.isEmpty()){
System.out.println(“请提供作业详细信息:”);
扫描仪=新的扫描仪(System.in);
字符串作业=scanner.nextLine();
jobs.add(job);
scanner.close();
jobs.notifyAll();
睡眠(4000);
}否则{
jobs.wait();
}
}捕捉(中断异常e){
e、 printStackTrace();
}
}
}
}
}
包测试线程;
导入java.util.List;
公共类使用者实现可运行{
私有列表作业=null;
公众消费者(名单){
this.jobs=列表;
}
@凌驾
公开募捐{
while(true)
{
已同步(作业){
试一试{
if(jobs.isEmpty()){
jobs.wait();
}否则{
for(字符串作业:作业){
System.out.println(“作业完成:+作业”);
删除(作业);
《睡眠》(2000年);
}
jobs.notifyAll();
}
}捕捉(中断异常e){
e、 printStackTrace();
}
}
}
}
}
包测试线程;
导入java.util.ArrayList;
导入java.util.List;
公共类测试线程{
公共静态void main(字符串…字符串)
{
列表=新的ArrayList();
生产者=新生产者(列表);
消费者=新消费者(列表);
线程prodThread=新线程(生产者,“生产者”);
线程constread=新线程(consumer,“consumer”);
prodThread.start();
constread.start();
}
}

不能从使用增强型for语句迭代的列表中删除项目

如果要删除元素,则需要一个显式迭代器来调用remove:

Iterator<String> it = jobs.iterator();
while (it.hasNext()) {
  String job = it.next();
  System.out.println("Job completed: " + job);
  it.remove();
  Thread.sleep(2000);
}
Iterator it=jobs.Iterator();
while(it.hasNext()){
字符串job=it.next();
System.out.println(“作业完成:+作业”);
it.remove();
《睡眠》(2000年);
}

当然,像这样从ArrayList中删除项是非常低效的(这是
O(n^2)
),而且很容易出错,因为必须显式同步。您应该考虑使用某种类型的
阻塞队列

您得到的ConcurrentModificationException不是因为并发:您只需删除循环中的项:

for (String job : jobs) {
     System.out.println("Job completed: " + job);
     jobs.remove(job);

使用迭代器删除项。

您的并发修改不是由线程引起的

    for (String job : jobs) {
                        System.out.println("Job completed: " + job);
                        jobs.remove(job);
                        Thread.sleep(2000);
                    }
作业。删除(作业)在迭代ArrayList时修改它。迭代器仅允许在使用迭代器删除方法完成此操作时修改列表

     for( Iterator<String> iter = jobs.iterator(); iter.hasNext(); )
     {
           String job = iter.next();
           iter.remove();
     } 
for(迭代器iter=jobs.Iterator();iter.hasNext();)
{
字符串job=iter.next();
iter.remove();
} 

尽管您得到了一个
ConcurrentModificationException
但您的问题根本不是由多线程引起的

事实上,这就是问题代码:

for (String job : jobs) {
    System.out.println("Job completed: " + job);
    jobs.remove(job);
    Thread.sleep(2000);
}
for
循环将在作业上使用
迭代器
,同时您可以直接(从结构上)修改
作业
(即,不通过
迭代器
)。这意味着,
迭代器
不再是定义良好的迭代器,它抛出此异常来告诉您

如果在迭代集合时需要从集合中删除元素,则需要使
迭代器显式化:

Iterator<String> jobIterator = job.iterator();
while (jobIterator.hasNext()) {
  String job = jobIterator.next();
  // ... stuff ...
  jobIterator.remove(); // remove the last object that was returned by next()
}
Iterator jobIterator=job.Iterator();
while(jobIterator.hasNext()){
字符串job=jobIterator.next();
//…东西。。。
jobIterator.remove();//删除next()返回的最后一个对象
}

您应该使用
CopyOwWriteArrayList
以防止在使用list
迭代器时出现
ConcurrentModificationException
(对于代码中的每个循环,在引擎盖下使用它)

无法删除元素,而请使用迭代器方法删除

Iterator<String> it = jobs.iterator();
while(it.hasNext()) {
      String job = it.next();
      ....
      it.remove();
      ...
}
Iterator it=jobs.Iterator();
while(it.hasNext()){
字符串job=it.next();
....
it.remove();
...
}

这将避免concurrentModificationException。

必须在同一实例上同步对ArrayList的所有访问。你的代码不是这样的(例如getter和setter)。你能详细说明一下吗?@AndyTurner啊,收回了我的评论。