1. Fail-fast Introduction
"Fast Fail" is fail-fast, which is an error detection mechanism for Java collections. When a thread iterates over collection, it does not allow other threads to make structural changes to the collection.
For example, suppose there are two threads (thread 1, thread 2, thread 1 through the iterator in the traversal of the elements of set a, at some point, thread 2 modifies the structure of set a (which is the modification of the structure, rather than simply modifying the contents of the collection Element), then the program throws Concurrentmodificationexception abnormal, resulting in fail-fast.
The rapid failure behavior of an iterator is not guaranteed, and it does not guarantee that this error will occur, so concurrentmodificationexception should be used only to detect bugs.
All collection classes in the Java.util package are fast failures, and the collection classes in the Java.util.concurrent package are security failures;
A fast-failing iterator throws Concurrentmodificationexception, and a security-failed iterator never throws the exception.
2 Fail-fast Sample
Sample code: (Fastfailtest.java)
Import java.util.*;
Import java.util.concurrent.*;
/* * @desc the Fast-fail test program in the Java collection. * * Fast-fail Event conditions: When multiple threads operate on collection, if one of the threads traverses the collection through iterator, the contents of the collection are changed by other threads
; The concurrentmodificationexception exception is thrown.
* Fast-fail Solution: The Fast-fail event is not generated by handling the corresponding class under the Util.concurrent collection package. * * In this case, test the ArrayList and Copyonwritearraylist respectively.
ArrayList produces Fast-fail events, and Copyonwritearraylist does not produce fast-fail events. * (01) When using ArrayList, it will produce Fast-fail event, throw Concurrentmodificationexception exception, and define as follows: * private static list<string> List
= new Arraylist<string> (); * (02) When used copyonwritearraylist, does not produce fast-fail event; the definition is as follows: * private static list<string> List = new Copyonwritearraylis
T<string> (); * * @author Skywang * * * public class Fastfailtest {private static list<string> List = new Arraylist<string>
;();
private static list<string> List = new copyonwritearraylist<string> ();
public static void Main (string[] args) {//Start two threads to operate the list at the same time!
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 (value+ ",");
}/** * Adds a 0,1,2,3,4,5 to the list followed by Printall () to traverse the entire list/private static class Threadone extends Thread {
public void Run () {int i = 0;
while (i<6) {List.add (string.valueof (i));
Printall ();
i++; }}/** * Adds 10,11,12,13,14,15 to the list, and after each addition, it traverses the entire list via printall () (), private static class Threadtwo extends
Thread {public void run () {int i = 10;
while (i<16) {List.add (string.valueof (i));
Printall ();
i++;
}
}
}
}
Run results
Run the code, throw an exception java.util.concurrentmodificationexception! That is, produce Fail-fast event!
Result description
() Fastfailtest the new Threadone (). Start () and New Threadtwo (). Starting () starts two threads at the same time to manipulate the list.
Threadone Thread: Add 0,1,2,3,4,5 to the list in turn. After each additional number is added, the entire list is traversed by printall ().
Threadtwo Thread: Add 10,11,12,13,14,15 to the list in turn. After each additional number is added, the entire list is traversed by printall ().
(02) When a thread traverses the list, the contents of the list are changed by another thread, and a Concurrentmodificationexception exception is thrown, resulting in a fail-fast event.
3. Fail-fast Solutions
fail-fast mechanism is a kind of error detection mechanism. It can only be used to detect errors because the JDK does not guarantee that the fail-fast mechanism will occur. If you use the fail-fast mechanism in a multithreaded environment, it is recommended to replace the "classes under Java.util package" with the "classes under Java.util.concurrent".
So, in this case, just replace the ArrayList with the corresponding class in the Java.util.concurrent package. That is, the code
private static list<string> List = new arraylist<string> ();
Replaced by
private static list<string> List = new copyonwritearraylist<string> ();
The solution can be solved.
The
4. Fail-fast principle
produces fail-fast events that are triggered by throwing concurrentmodificationexception exceptions.
So, how does ArrayList throw concurrentmodificationexception exceptions?
We know that Concurrentmodificationexception is an exception that is thrown when the iterator is manipulated. Let's take a look at iterator's source code first. The iterator of ArrayList is implemented in the parent class Abstractlist.java. The code is as follows:
Package java.util;
Public abstract class Abstractlist<e> extends abstractcollection<e> implements list<e> {...//Abs
Tractlist unique attributes//to record the number of changes to the list: every modification (add/delete, etc.), the modcount+1 protected transient int modcount = 0; Returns the list corresponding iterator.
In effect, it returns the Itr object.
Public iterator<e> iterator () {return new Itr ();
}//ITR is the implementation class of iterator (iterator) Private class Itr implements iterator<e> {int cursor = 0;
int lastret =-1;
The record value of the modified number. Each time a new ITR () object is created, the corresponding Modcount is saved when the new object is created;//every time the elements in the list are traversed, the expectedmodcount and modcount are equal;//if Not equal,
The concurrentmodificationexception exception is thrown, resulting in a fail-fast event.
int expectedmodcount = Modcount;
public Boolean Hasnext () {return cursor!= size (); When public E next () {//Gets the next element, it determines whether the "Modcount" and "current Modcount" saved when the new ITR object is equal;//if not equal, throw Concurrentmodificationex
Ception exception, resulting in Fail-fast event.
Checkforcomodification ();
try {E next = get (cursor);
Lastret = cursor++;
return to Next;
catch (Indexoutofboundsexception e) { Checkforcomodification ();
throw new Nosuchelementexception ();
} public void Remove () {if (Lastret = 1) throw new IllegalStateException ();
Checkforcomodification ();
try {AbstractList.this.remove (Lastret);
if (Lastret < cursor) cursor--;
Lastret =-1;
Expectedmodcount = Modcount;
catch (Indexoutofboundsexception e) {throw new concurrentmodificationexception (); } final void Checkforcomodification () {if (Modcount!= expectedmodcount) throw new Concurrentmodificationex
Ception ();
}
}
...
}
From there, we can see that the checkforcomodification () is executed when the next () and remove () are invoked. If "Modcount is not equal to Expectedmodcount", the concurrentmodificationexception exception is thrown, resulting in the Fail-fast event.
to understand the fail-fast mechanism, we need to understand when "modcount is not equal to Expectedmodcount"!
from the ITR class, we know that Expectedmodcount is assigned modcount when the ITR object is created. Through ITR, we know: Expectedmodcount cannot be modified to not equal to Modcount. Therefore, the need for textual research is modcount when will be modified.
Next, we look at ArrayList's source code to see how the Modcount was modified.
Package java.util; public class Arraylist<e> extends abstractlist<e> implements List<e>, Randomaccess, Cloneable, JAVA.I
The corresponding synchronization function is public void ensurecapacity (int mincapacity) {modcount++ When capacity changes in o.serializable//list.
int oldcapacity = Elementdata.length;
if (Mincapacity > Oldcapacity) {Object olddata[] = elementdata;
int newcapacity = (oldcapacity * 3)/2 + 1;
if (Newcapacity < mincapacity) newcapacity = mincapacity;
Mincapacity is usually the close to size, so this is a win:elementdata = arrays.copyof (Elementdata, newcapacity);
}///add element to queue last public boolean add (e e) {//Modify Modcount ensurecapacity (size + 1);//increments modcount!!
elementdata[size++] = e;
return true; //Add element to specified location public void Add (int index, E element) {if (Index > Size | | Index < 0) throw new Indexoutof
Boundsexception ("Index:" +index+ ", Size:" +size); Modification of Modcount ensurecapacity (size+1); Increments Modcount!!
System.arraycopy (Elementdata, index, Elementdata, index + 1, size-index);
Elementdata[index] = element;
size++;
}//Add Collection public boolean addall (collection<? extends e> c) {object[] a = C.toarray ();
int numnew = A.length; Modify Modcount ensurecapacity (size + numnew);
Increments Modcount system.arraycopy (A, 0, elementdata, size, numnew);
Size + = Numnew;
return numnew!= 0;
//Deletes the element public E remove (int index) {rangecheck (index) at the specified location;
Modify 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;
Let GC does its work return oldValue;
///quickly delete the element at the specified location private void fastremove (int index) {//modify Modcount modcount++;
int nummoved = size-index-1;
if (nummoved > 0) system.arraycopy (elementdata, index+1, Elementdata, Index, nummoved); ElementData[--size] = null;
Let GC does its work}//Empty set public void clear () {//modify Modcount modcount++;
Let GC does its work for (int i = 0; i < size; i++) elementdata[i] = null;
size = 0;
}
...
}
From this, we find that either add (), remove (), or clear () changes the value of the Modcount as long as it involves modifying the number of elements in the collection.
Next, let's comb the system to see how the Fail-fast is produced. The steps are as follows:
(01) A new ArrayList is created with the name ArrayList. The
(02) adds content to the ArrayList.
(03) Creates a new "thread a" and reads the ArrayList value repeatedly through iterator in thread A.
(04) Creates a new "thread B" and deletes a "Node A" in ArrayList in thread B.
(05) At this point, an interesting event will be generated.
Thread A at some point created the ArrayList iterator. At this point, "Node A" still exists in ArrayList, and when the ArrayList is created, Expectedmodcount = Modcount (assuming that they now have a value of N).
Thread B was executed at some point in the traversal of the ArrayList process by thread A, and thread B removes node A in ArrayList. When thread B performs the remove (), the "modcount++" is executed in remove (), when Modcount becomes n+1!
Thread A is then traversed, and when it executes to the next () function, the call to Checkforcomodification () compares the size of "Expectedmodcount" and "Modcount", and "expectedmodcount=n "," modcount=n+1 ", thus, throws Concurrentmodificationexception exception, produces Fail-fast event.
So far, we have a complete understanding of how fail-fast is produced!
that is, when multiple threads operate on the same collection, the contents of the collection are changed by other threads when the thread accesses the collection (that is, other threads change the value of the Modcount by means of Add, remove, clear, and so on); The concurrentmodificationexception exception is thrown and the Fail-fast event is generated.
5. The principle of resolving the Fail-fast
above, explained the "solution to the fail-fast mechanism", also know the "fail-fast the root cause." Next, let's talk a little more about how the Fail-fast event is resolved in the Java.util.concurrent package. The
is also explained by the copyonwritearraylist corresponding to the ArrayList. Let's take a look at the source code of Copyonwritearraylist:
Package java.util.concurrent;
Import java.util.*;
Import java.util.concurrent.locks.*;
Import Sun.misc.Unsafe; public class copyonwritearraylist<e> implements List<e>, Randomaccess, cloneable, java.io.Serializable {..
. Returns the iterator public iterator<e> iterator () of the collection corresponding to the Fast-fail implementations in the return new collection class, let's take the simplest ArrayList for example. protected transient int modcount = 0; records the number of times we modified the ArrayList, such as when we call Add (), remove (), and so on, we will modcount++. protected transient int modcount = 0; records the number of times we modified the ArrayList, such as when we call Add (), remove (), and so on, we will modcount++.
Cowiterator<e> (GetArray (), 0);
private static class Cowiterator<e> implements Listiterator<e> {private final object[] snapshot;
private int cursor;
Private Cowiterator (object[] elements, int initialcursor) {cursor = Initialcursor;
When you create a new cowiterator, the elements in the collection are saved to a new copy array.
In this way, when the data in the original collection changes, the values in the copied data do not change.
snapshot = elements;
public Boolean Hasnext () {return cursor < snapshot.length; public Boolean hasprevious () {return cursor > 0;
Public E Next () {if (! Hasnext ()) throw new Nosuchelementexception ();
Return (E) snapshot[cursor++];
Public E Previous () {if (! hasprevious ()) throw new Nosuchelementexception ();
Return (E) snapshot[--cursor];
public int Nextindex () {return cursor;
public int Previousindex () {return cursor-1;
public void Remove () {throw new unsupportedoperationexception ();
public void Set (E e) {throw new unsupportedoperationexception ();
public void Add (e e) {throw new unsupportedoperationexception ();
}
}
...
}
From this, we can see:
(01) and ArrayList inherited from Abstractlist different, Copyonwritearraylist did not inherit from the abstractlist, it just implemented the list interface.
The iterator returned by the iterator () function of ArrayList is implemented in Abstractlist, and Copyonwritearraylist is implemented by itself.
() When you call next () in the iterator implementation class of ArrayList, you will "invoke checkforcomodification () to compare the size of ' expectedmodcount ' and ' modcount '"; Copyonwritearraylist iterator implementation class, there is no so-called checkforcomodification (), not to throw concurrentmodificationexception exception!
6. Summary
because HashMap (ArrayList) is not thread-safe, if there are other threads in the process of using iterators that modify the map (the modification here refers to structural modification, not the element that simply modifies the contents of the collection), Then the Fail-fast policy will be thrown concurrentmodificationexception
Mainly through the Modcount domain to achieve, to ensure that the visibility between threads, modcount that is the number of changes, for HashMap (ArrayList) content changes will increase this value, This value is then assigned to the iterator's expectedmodcount during initialization of the iterator.
But fail-fast behavior is not guaranteed, so the procedure that relies on this exception is wrong