Example of exception in ArrayList?

2019-08-28 23:15发布

I am using ArrayList and I want an example of Exception in case if multiple threads try to access the same list without synchronization ? I done this in single threaded application in which if we remove an element from list while iteration it throws ConcurrentModificationExceptoin but I want to achieve the same in multi threaded environment. If anyone could give me an example of that would be highly appreciated ?

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




    }

}

4条回答
Melony?
2楼-- · 2019-08-28 23:36

it throws ConcurrentModificationExceptoin but I want to achieve the same in multi threaded environment

I am asking how to get an exception in multithreaded environment - From comment

Since you are creating separate copy of List for each thread hence there is no chance to get this exception.

Just make the List as shared resource then you will encounter this exception:

sample code:

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

Output:

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)
查看更多
啃猪蹄的小仙女
3楼-- · 2019-08-28 23:39

You're accessing a separate list instance in each of your threads. Since each list is only accessed by one thread, you can't get a concurrency error.

List list = new ArrayList<String>();

That declares an instance field. Therefore, each call to new ThreadTest() creates a new list. In order to make all the ThreadTest instances use the same list, try making the field static (i.e. a class field):

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

As for how an error can happen, take a look at the code for ArrayList's add method:

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

If two threads call add at the same time, they could process the elementData[size++] = e statement at the same time. The size field is not declared volatile; therefore, the two threads could end up writing to the same index in the elementData array.

Even if size were declared volatile, the size++ operation is not atomic. See How to model a situation, when i++ is corrupted by simultaneously executing threads? for an example of how an operation like size++ can fail in a multithreaded environment.

Finally, if you don't understand what volatile and atomic mean in the Java context, you really need to read up on concurrent programming in Java before you write any multithreaded code. It will be a worthwhile investment as you'll save yourself a lot of headaches by understanding these concepts.

查看更多
孤傲高冷的网名
4楼-- · 2019-08-28 23:47

Quick answer:

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

Output:

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)

Note: As @Braj and @DaoWen said you are using different instances. Either use their suggestions or pass the list variable in the constructor of your class (ThreadTest).

查看更多
来,给爷笑一个
5楼-- · 2019-08-28 23:48

If I understand your question, yes change this

List list = new ArrayList<String>();

by using Collections.synchronizedList(List) to something like (and don't use raw types),

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

From the javadoc,

Returns a synchronized (thread-safe) list backed by the specified list. In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list.

It is imperative that the user manually synchronize on the returned list when iterating over it:

List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
  Iterator i = list.iterator(); // Must be in synchronized block
  while (i.hasNext())
    foo(i.next());
  }
}
查看更多
登录 后发表回答