Пример исключения в ArrayList?
Я использую 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();
}
}
}
4 ответа
Вы получаете доступ к отдельному экземпляру списка в каждой из ваших тем. Поскольку каждый список доступен только одному потоку, вы не можете получить ошибку параллелизма.
List list = new ArrayList<String>();
Это объявляет поле экземпляра. Поэтому каждый звонок new ThreadTest()
создает новый список. Для того, чтобы сделать все ThreadTest
экземпляры используют тот же список, попробуйте сделать поле static
(т.е. поле класса):
static List list = new ArrayList<String>();
Что касается того, как может произойти ошибка, взгляните на код для ArrayList
"s add
метод:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Если две темы называют add
в то же время они могли бы обрабатывать elementData[size++] = e
заявление в то же время. size
поле не объявлено volatile
; следовательно, два потока могут закончить запись в один и тот же индекс в elementData
массив.
Даже если size
были объявлены volatile
, size++
операция не атомарная. См. Как смоделировать ситуацию, когда i++ поврежден из-за одновременного выполнения потоков? для примера того, как операция, как size++
может потерпеть неудачу в многопоточной среде.
Наконец, если вы не понимаете, что volatile
и атомарное среднее в контексте Java, вам действительно нужно ознакомиться с параллельным программированием в Java, прежде чем писать какой-либо многопоточный код. Это будет выгодное вложение, поскольку вы избавите себя от множества головных болей, поняв эти концепции.
Быстрый ответ:
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).
Если я понимаю ваш вопрос, да измените это
List list = new ArrayList<String>();
используя Collections.synchronizedList(List)
что-то вроде (и не используйте сырые типы),
List<String> list = Collections.synchronizedList(new ArrayList<String>());
Из Javadoc,
Возвращает синхронизированный (потокобезопасный) список, поддерживаемый указанным списком. Чтобы гарантировать последовательный доступ, очень важно, чтобы весь доступ к списку поддержки осуществлялся через возвращенный список.
Крайне важно, чтобы пользователь вручную синхронизировал возвращаемый список при выполнении итерации по нему:
List list = Collections.synchronizedList(new ArrayList()); ... synchronized (list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); } }
он выбрасывает 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)