【问题标题】:Example of exception in ArrayList?ArrayList 中的异常示例?
【发布时间】:2014-07-25 03:26:20
【问题描述】:

我正在使用 ArrayList,如果多个线程尝试在不同步的情况下访问同一个列表,我想要一个异常示例?我在单线程应用程序中完成了此操作,如果我们在迭代时从列表中删除一个元素,它会抛出 ConcurrentModificationExceptoin 但我想在多线程环境中实现相同的目标。如果有人能给我一个例子,将不胜感激?

package com.test2;

public class ThreadTest extends Thread {

    List list = new ArrayList<String>();

    @Override
    public void run() {

        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        list.add("6");
        list.add("7");
        list.add("8");

        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

    }

    public static void main(String[] args) {



        Thread th1 = new ThreadTest();
        Thread th2 = new ThreadTest();
        Thread th3 = new ThreadTest();
        th1.start();
        th2.start();
        th3.start();
        try {
            th1.join();
            th2.join();
            th3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }




    }

}

【问题讨论】:

  • 最好也分享您的代码。
  • 您是在问如何解决这个 ConcurrentModificationExceptoin 问题?
  • Braj : 我在问如何在多线程环境中获取异常
  • 就像你在单线程环境中做的那样。
  • 你可能想要一个for(int i=0; i&lt;1000000; i++) { list.add(i); },而不是只有 8 个元素并希望创建一个并发线程问题。

标签: java multithreading collections


【解决方案1】:

您在每个线程中访问一个单独的列表实例。由于每个列表只能由一个线程访问,因此不会出现并发错误。

List list = new ArrayList<String>();

声明一个实例字段。因此,每次调用new ThreadTest() 都会创建一个新列表。为了使所有ThreadTest 实例使用相同的列表,请尝试使字段static(即类字段):

static List list = new ArrayList<String>();

至于如何会发生错误,看看the code for ArrayList's add method

 public boolean add(E e) {
     ensureCapacityInternal(size + 1);  // Increments modCount!!
     elementData[size++] = e;
     return true;
 }

如果两个线程同时调用add,它们可以同时处理elementData[size++] = e语句。 The size field is not declared volatile;因此,两个线程最终可能会写入elementData 数组中的相同索引。

即使size 被声明为volatilesize++ 操作不是原子的。请参阅 How to model a situation, when i++ is corrupted by simultaneously executing threads? 以了解像 size++ 这样的操作如何在多线程环境中失败的示例。

最后,如果您不了解 volatile 和 atomic 在 Java 上下文中的含义,那么您确实需要在编写任何多线程代码之前阅读 Java 中的并发编程。这将是一项值得的投资,因为通过了解这些概念,您可以省去很多麻烦。

【讨论】:

  • 非常感谢道文。添加静态后我得到异常。
【解决方案2】:

快速回答:

public class Main {

    public static void main(String[] args) throws InterruptedException
    {
        final ArrayList<String> list = new ArrayList<String>();
        list.add("Item 1");
        list.add("Item 2");
        list.add("Item 3");
        list.add("Item 4");

        Thread thread = new Thread(new Runnable()
        {
            @Override
            public void run ()
            {
                for (String s : list)
                {
                    System.out.println(s);
                    try
                    {
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();

        Thread.sleep(2000);
        list.remove(0);
    }
}

输出:

Item 1
Item 2
Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at com.akefirad.tests.Main$1.run(Main.java:34)
    at java.lang.Thread.run(Thread.java:745)

注意: 正如@Braj 和@DaoWen 所说,您使用的是不同的实例。要么使用他们的建议,要么在你的类 (ThreadTest) 的构造函数中传递列表变量。

【讨论】:

    【解决方案3】:

    如果我理解你的问题,是的,改变这个

    List list = new ArrayList<String>();
    

    通过将Collections.synchronizedList(List) 用于类似(并且不要使用raw types),

    List<String> list = Collections.synchronizedList(new ArrayList<String>());
    

    来自 javadoc,

    返回由指定列表支持的同步(线程安全)列表。为了保证串行访问,所有对backing list的访问都是通过返回的list来完成的。

    用户在迭代返回的列表时必须手动同步它:

    List list = Collections.synchronizedList(new ArrayList());
    ...
    synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
        foo(i.next());
      }
    }
    

    【讨论】:

    • 感谢 Elliott Frisch 提供的代码,但我正在尝试获取异常。我正在对 Java Doc 语句进行 POC “如果我们在多线程环境中使用 ArrayList,我们将得到一个并发异常。”
    【解决方案4】:

    它抛出 ConcurrentModificationExceptoin 但我想在多线程环境中实现同样的效果

    我在问如何在多线程环境中获取异常 - 来自评论

    由于您正在为每个线程创建单独的 List 副本,因此没有机会获得此异常。

    只要将List设为共享资源,你就会遇到这个异常:

    示例代码:

    public class Main{
    
        public static void main(String[] args){
    
            // shared by all the threads.
            final List<String> list = new ArrayList<String>();
            
            class ThreadTest extends Thread {
    
    
                @Override
                public void run() {
    
                    list.add("1");
                    list.add("2");
                    list.add("3");
                    list.add("4");
                    list.add("5");
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    list.add("6");
                    list.add("7");
                    list.add("8");
    
                    Iterator<String> it = list.iterator();
                    while (it.hasNext()) {
                        System.out.println(it.next());
                    }
    
                }
            }
    
            Thread th1 = new ThreadTest();
            Thread th2 = new ThreadTest();
            Thread th3 = new ThreadTest();
            th1.start();
            th2.start();
            th3.start();
            try {
                th1.join();
                th2.join();
                th3.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    输出:

    Exception in thread "Thread-2" java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
        at java.util.ArrayList$Itr.next(Unknown Source)
        at com.test.TestDemo$1ThreadTest.run(TestDemo.java:390)
    

    【讨论】:

    • Braj : 你能告诉我你在哪里申报你的名单吗?
    • 就在类ThreadTest之外,使其共享资源。
    • 恐怕我们可以在Java的类之外声明一个列表。它给出了编译错误。你确定我们可以在 Java 的类之外声明列表吗?
    • 不知道怎么弄的,我分享了一个运行代码。
    猜你喜欢
    • 2014-08-05
    • 1970-01-01
    • 1970-01-01
    • 2012-08-31
    • 1970-01-01
    • 2019-03-31
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多