1. Введение в неудачу
«Быстрый отказ» также называется Fail-Fast, который является механизмом обнаружения ошибок для коллекций Java. Когда поток итерации над коллекцией, другим потокам не разрешается структурно изменять коллекцию.
Например: предположим, что существует два потока (поток 1 и поток 2), а резьба 1 пересекает элементы в установленном итераторе. В какой-то момент поток 2 изменяет структуру SET A (модификация структуры, а не просто изменяя содержимое элемента SET), тогда программа будет выбросить исключение ConcurentModificationException, тем самым генерируя ошибку.
Быстрое поведение итератора не может быть гарантировано, оно не может гарантировать, что ошибка произойдет, поэтому для обнаружения ошибок следует использовать только для обнаружения ошибок.
Все классы сбора в пакете java.util быстро провалились, в то время как классы сбора в пакете java.util.concurrent безопасно проходят;
Итератор, который не удается, быстро бросает concurrentmodificationException, в то время как итератор, который не сбои, никогда не бросает это исключение.
2 неудачные примеры
Пример кода: (fastfailtest.java)
Import java.util.*; импортировать java.util.concurrent.*;/** @dest тестовая программа для быстрого подготовки в коллекции Java. * * Условия для быстрого события: когда несколько потоков работают в коллекции, если одна из потоков проходит с коллекции через итератор, содержание коллекции изменяется другими потоками; Исключение CondurentModificationException будет брошено. * Решение быстрого подготовки: если вы обрабатываете его через соответствующий класс в пакете сбора UTIL.Concurrent, событие быстрого подхода не будет сгенерировано. * * В этом примере два случая ArrayList и CopyonWritearRayList протестированы соответственно. ArrayList будет генерировать быстрое событие, в то время как CopeonWritearRaylist не будет генерировать быстрое событие. * (01) При использовании ArrayList будет сгенерировано событие быстрого подготовки, и будет выброшено исключение CondurentModificationException; Определение следующим образом: * Частный статический список <string> list = new ArrayList <string> (); * (02) При использовании CopeonWritearRayList событие быстрого подготовки не будет сгенерировано; Определение следующим образом: * Частный статический список <string> list = new CopyonWritearRayList <String> (); * * @author Skywang */public Class FastFailTest {Private Static List <string> list = new ArrayList <string> (); // частный статический список <string> list = new CopeonWritearRayList <String> (); public static void main (string [] args) {// одновременно запустить два потока, чтобы работать в списке! new Threadone (). start (); new Threadtwo (). start (); } private static void printall () {System.out.println (""); String value = null; Iterator iter = list.iterator (); while (iter.hasnext ()) {value = (string) iter.next (); System.out.print (значение+","); }} /*** Добавить 0,1,2,3,4,5 в список по очереди. После добавления числа итерации через prantall () */ private Static Class Threadone Extends indues {public void run () {int i = 0; while (i <6) {list.add (string.valueof (i)); prantall (); i ++; }}} /*** Добавить 10,11,12,13,14,15 в список по очереди. После того, как каждое число будет добавлено, он пройдет весь список через prantall () */ private Static Class Threadtwo Extends Thread {public void run () {int i = 10; while (i <16) {list.add (string.valueof (i)); prantall (); i ++; }}}} Запустите код в результате и бросает исключение java.util.concurrentmodificationexception! То есть создается неудачное событие!
Результаты Описание
(01) В FastfailTest запустите два потока одновременно, чтобы управлять списком через new Threadone (). Start () и new Threadtwo (). Start ().
Threadone Thread: добавьте 0, 1, 2, 3, 4, 5 в список. После того, как каждое число добавлено, весь список проходит через Printall ().
Threadtwo Thread: добавьте 10, 11, 12, 13, 14, 15 в список. После того, как каждое число добавлено, весь список проходит через Printall ().
(02) Когда поток пересекает список, содержание списка изменяется другим потоком; Будет брошено исключение CondurentModificationException, что приведет к неудачному событию.
3. Неудачное решение
Механизм сбои-это механизм обнаружения ошибок. Он может использоваться только для обнаружения ошибок, потому что JDK не гарантирует, что механизм неудачного отказа произойдет. Если вы используете коллекцию ошибочного механизма в многопоточной среде, рекомендуется использовать «классы в пакете java.util.concurrent» для замены «классов в пакете java.util».
Поэтому в этом примере вам нужно только заменить ArrayList соответствующим классом в пакете Java.Util.concurrent. То есть код
Частный статический список <string> list = new ArrayList <string> ();
Заменить на
Частный статический список <string> list = new CopeonWritearRayList <String> ();
Это решение может быть решено.
4. Неудачный принцип
Сгенерировано сбоееотрагированным событием, которое запускается путем выброса исключения condurentModificationException.
Итак, как ArrayList бросает исключение ConcurrentModificationException?
Мы знаем, что ConcurrentModificationException является исключением, брошенным при эксплуатации итератора. Давайте сначала посмотрим на исходный код итератора. Итератор ArrayList реализован в родительском классе AbstractList.java. Код заключается в следующем:
Пакет java.util;
Public Abstract Class AbstractList <e> расширяет AbstractCollection <e> реализует список <e> {... // Уникальный атрибут в AbstractList // Используется для записи количества измененных списков: каждый раз (добавление/удаление операций и т. Д.), Modcount+1 защищенный Transient int modcount = 0; // вернуть итератор для списка. На самом деле, это вернуть объект ITR. public iterator <e> iterator () {return new itr (); } // ITR - это класс реализации итератора (итератор). Частный класс ITR реализует итератор <e> {int cursor = 0; int lastret = -1; // Измените значение записи номера. // Каждый раз, когда создается новый объект itr (), будет сохранен соответствующий ModCount при создании нового объекта; // Каждый раз, когда вы пересекаете элементы в списке, вы будете сравнивать, являются ли ожидаемые модкинты и modcount; // Если не равен, то исключение ConcurrentModificationException выдвигается, что приводит к неудачному событию. int weddcount = modcount; public boolean hasnext () {return cursor! = size (); } public e Next () {// Перед получением следующего элемента будет оценено, равны ли «modCount при создании нового объекта ITR» и «текущий modcount»; // Если это не равное, то исключение concurrentModificationException выдвигается, что приводит к неудачному событию. checkforComodification (); попробуйте {e next = get (cursor); lastret = cursor ++; вернуться следующим; } catch (indexoutOfBoundSexception e) {checkforComodification (); бросить новое noshelementexception (); }} public void remove () {if (lastret == -1) бросить новый allodalStateException (); checkforComodification (); try {AbstractList.This.remove (lastret); if (lastret <cursor) cursor--; lastret = -1; weddcount = modcount; } catch (indexoutOfBoundSexception e) {бросить новый concurrentModificationException (); / }} ...} Исходя из этого, мы можем обнаружить, что CheckForComodification () выполняется, когда вызовут Next () и Remove (). Если «Modcount не равен ожидаемому модкоунт», исключение CondurentModificationException, что приводит к неудачному событию.
Чтобы понять ошибочный механизм, мы должны понять, когда «ModCount не равняется ожидаемому модушке»!
Из класса ITR мы знаем, что ожидаемый ModCount назначается ModCount при создании объекта ITR. Благодаря ITR мы знаем, что ожидаемый модкоунт не может быть изменен, чтобы не равный modcount. Следовательно, необходимо проверить, когда ModCount будет изменен.
Далее, давайте проверим исходный код ArrayList, чтобы увидеть, как изменяется ModCount.
пакет java.util; открытый класс ArrayList <e> расширяет AbstractList <e> Список реализации <e>, случайный, клонируемый, java.io.serializable {... // Когда изменяется емкость в списке, соответствующая функция синхронизации public voidecapatice (int mincapacity) {modcount ++; int oldCapacity = elementData.length; if (mincapacity> oldcapacity) {Object oldData [] = elementData; int newcapacity = (OldCapacity * 3)/2 + 1; if (newcapacity <mincapacity) newcapacity = mincapacity; // mincapacity обычно близка к размеру, так что это победа: elementData = arrays.copyof (elementdata, newcapacity); }} // Добавить элемент к последнему из очереди public boolean add (e e) {// modcount evureCapacity (размер + 1); // Приращивание ModCount !! elementData [size ++] = E; вернуть истину; } // Добавить элемент в указанное местоположение public void add (int index, e element) {if (index> size || index <0). Выбросить новый indexoutofboundsexception ("index:"+index+", size:"+size); // Модифицировать ModCount EnsureCapacity (размер+1); // Приращивание ModCount !! System.ArrayCopy (ElementData, Index, ElementData, Index + 1, Size - Index); elementData [index] = element; размер ++; } // Добавить коллекцию public boolean addall (collection <? Extends e> c) {object [] a = c.toarray (); int numnew = a.length; // Модифицировать modcount evureCapacity (размер + numnew); // увеличивает modcount System.ArrayCopy (a, 0, elementdata, size, numnew); размер += numnew; вернуть numnew! = 0; } // Удалить элемент в указанном месте public e Remove (int index) {rangecheck (index); // изменять modcount modcount ++; E oldValue = (e) elementData [index]; int nummoved = size - index - 1; if (Nummoved> 0) System.ArrayCopy (elementData, index+1, elementData, index, Nummoved); elementData [-size] = null; // Пусть GC сделает свою работу вернуть OldValue; } // Быстрое удаление элементов в указанном месте Private void fastremove (int index) {// изменять modcount modcount ++; int nummoved = size - index - 1; if (Nummoved> 0) System.ArrayCopy (elementData, index+1, elementData, index, Nummoved); elementData [-size] = null; // Позвольте GC выполнить свою работу} // Очистить коллекцию public void clear () {// modcount modcount ++; // Пусть GC выполнит свою работу для (int i = 0; i <size; i ++) elementdata [i] = null; размер = 0; } ...} Исходя из этого, мы обнаружили, что будь то Add (), Remove () или clear (), значение ModCount будет изменено до тех пор, пока оно включает в себя изменение количества элементов в наборе.
Далее, давайте систематически разберемся, как производится Fail-Fast. Шаги следующие:
(01) Создайте новое имя ArrayList в качестве ArrayList.
(02) Добавьте контент в ArrayList.
(03) Создайте новый «поток A» и многократно прочитайте значение ArrayList через итератор в «потоке A».
(04) Создайте новый «поток B» и удалите «узел A» в ArrayList в «потоке B».
(05) В настоящее время будут происходить интересные события.
В какой -то момент «нить А» создает итератор ArrayList. В это время «Уничтожить» все еще существует в Arraylist. При создании ArrayList, ожидаемый modcount = modcount (при условии, что их значения в настоящее время).
В какой -то момент во время процесса прохождения ArrayList «Thread B» выполняется, а «поток B» удаляет »узел« в массиве ». Когда «Поток B» выполняет remoad () для работы удаления, «Modcount ++» выполняется в remoad (), а ModCount становится n+1!
«Нить А», затем проходит. Когда он выполняет функцию Next (), CheckForComoDification () вызывается для сравнения размеров «ожидаемого модконта» и «modCount»; и «weardmodcount = n», «modcount = n+1», поэтому исключение CondurentModificationException, что приводит к неудачному событию.
На данный момент у нас есть полное понимание того, как производится неудача!
То есть, когда несколько потоков работают в одном и том же наборе, когда поток обращается к набору, содержимое набора изменяется другими потоками (то есть другие потоки изменяют значение modcount через добавление, удаление, очистки и другие методы); В настоящее время будет выпущено исключение CondurentModificationException, что приведет к неудачному событию.
5. Принцип решения неудачи.
Приведенное выше объясняется «методы решающих механизм неудач», а также знает «основную причину отказа». Далее, давайте поговорим о том, как решить провальное событие в пакете java.util.concurrent.
Давайте объясним это с помощью CopyonWritearRaylist, соответствующего ArrayList. Давайте сначала посмотрим на исходный код copyonWritearRaylist:
Package java.util.concurrent; import java.util.*; import java.util.concurrent.locks.*; import sun.misc.unsafe; открытый класс copeonWritearRaylist <e> Реализует список <e>, Randomaccess, Clonable, Java.io.serializable {// Возврат и ITERTORATO iterator () {вернуть методы реализации быстрого подготовки в новом классе коллекции почти одинаковы. Давайте возьмем самый простой ArrayList в качестве примера. Защищенный Transient int modcount = 0; Записывает количество раз, когда мы изменяем ArrayList. Например, когда мы вызовываем add (), remove () и т. Д. Чтобы изменить данные, Modcount ++ будет изменен. Защищенный Transient int modcount = 0; Записывает количество раз, когда мы изменяем ArrayList. Например, когда мы вызовываем add (), remove () и т. Д. Чтобы изменить данные, Modcount ++ будет изменен. Cowiterator <e> (getarray (), 0); } ... частный статический класс Cowiterator <e> реализует ListIterator <e> {Private Final Object [] Snapshot; частный курсор; Private Cowiterator (Object [] Elements, int initialCursor) {cursor = initialCursor; // При создании нового Cowiterator сохраните элементы в коллекции в новом массиве копирования. // Таким образом, когда данные исходного набора изменяются, значения в данных копирования также не изменятся. Snapshot = элементы; } public boolean hasnext () {return cursor <spanshot.length; } public boolean hasprevious () {return cursor> 0; } public e Next () {if (! HasNext ()) бросить новый noshelementException (); вернуть (e) снимок [cursor ++]; } public e предыдущий () {if (! hasprevious ()) бросить новый noshelementexception (); вернуть (e) снимок [-курсор]; } public int nextIndex () {return cursor; } public int int indIndex () {return cursor-1; } public void remove () {бросить новый UnsupPortedOperationException (); } public void set (e e) {бросить новый UnsupportedOperationException (); } public void add (e e) {бросить новый UnsupportedOperationException (); }} ...} Из этого мы можем увидеть:
(01) В отличие от ArrayList наследится от AbstractList, CopyOnWritearRayList не наследует от AbstractList, он только реализует интерфейс списка.
(02) Итератор, возвращаемый функцией итератора () ArrayList, реализован в AbstractList; в то время как CopyonWritearRaylist реализует сам итератор.
(03) Когда Next () вызывается в классе реализации итератора ArrayList, «Call CheckforComodification () для сравнения размеров« weddmodcount »и« modcount »; Тем не менее, в классе реализации итератора нет так называемого контроля CheckForComoDification () в классе реализации итератора CopyOnWritearRayList, и condurentModificationException не будет брошено!
6. Резюме
Поскольку HashMap (ArrayList) не является потоком, если другие потоки изменяют карту в процессе использования итератора (модификация здесь относится к структурной модификации, а не просто для изменения элементов содержимого сбора), тогда будет выброшена концепция condcountmodification Exception, то есть стратегия неудачи в основном осуществляется через поле модного поля для обеспечения визуальной резьбы. ModCount - это количество модификаций. Это значение будет добавлено к модификации контента HashMap (ArrayList). Затем во время инициализации итератора это значение будет назначено ожидаемому моднкоунту итератора.
Но неудачное поведение не гарантируется, поэтому практика полагаться на это исключение неверна