Java fail-fast mechanism

Source: Internet
Author: User
Tags concurrentmodificationexception

This article is reproduced from Cmsblogs Chenssy

Original address: http://cmsblogs.com/?p=1220

This article is only for my study reference use, if there is infringement immediately deleted.

In the collection of the JDK we often see something like this:

For example, ArrayList:

Note that the fast failure behavior of iterators is not guaranteed because, in general, it is not possible to make any hard guarantees as to whether or not there is a concurrency change in sync. A fast-failing iterator will do its best to throw concurrentmodificationexception. Therefore, it is a mistake to write a program that relies on this exception to improve the correctness of such iterators: The fast failure behavior of iterators should only be used to detect bugs.

In HashMap:

Note that the fast failure behavior of iterators is not guaranteed and, in general, there is no firm guarantee that there is an asynchronous concurrent modification. The fast-failing iterator does its best to throw concurrentmodificationexception. Therefore, it is wrong to write a program that relies on this exception, and the correct approach is that the fast failure behavior of the iterator should only be used to detect program errors.

Repeated references to "rapid failure" in these two paragraphs. So what is the "fast-failing" mechanism?

"Fast Failure", also known as Fail-fast, is an error detection mechanism for Java collections. The fail-fast mechanism is likely to occur when multiple threads are manipulating a collection for structural changes. Remember that it is possible, not necessarily. For example: Suppose there are two threads (thread 1, thread 2), thread 1 through iterator in the elements of the collection A, at some point, thread 2 modifies the structure of collection A (is the structure of the changes above, rather than simply modify the contents of the collection elements), then this time the program will throw Concurrentmodificationexception anomalies, resulting in a fail-fast mechanism.

One, fail-fast example
public class Failfasttest {private static list<integer> List = new arraylist<> ();      /** * @desc: Thread One Iteration list * @Project: Test * @file: Failfasttest.java * @Authro: Chenssy * @data: July 26, 2014 */private static class Threadone extends thread{public void run () {iterator<integer> it            Erator = List.iterator ();                while (Iterator.hasnext ()) {int i = Iterator.next ();                SYSTEM.OUT.PRINTLN ("Threadone traversal:" + i);                try {thread.sleep (10);                } catch (Interruptedexception e) {e.printstacktrace ();     }}}}/** * @desc: when i = = 3 o'clock, modify list * @Project: Test * @file: Failfasttest.java * @Authro: Chenssy * @data: July 26, 2014 */private static class Threadtwo extends thread{public void run ()             {int i = 0; while (I < 6) {System.out.prinTLN ("Threadtwo run:" + i);                if (i = = 3) {list.remove (i);            } i++; }}} public static void Main (string[] args) {for (int i = 0; i < 10;i++) {List.add        (i);        } new Threadone (). Start ();    New Threadtwo (). Start (); }}

Operation Result:

Threadone traversal: 0ThreadTwo run:0threadtwo run:1threadtwo run:2threadtwo run:3threadtwo run:4threadtwo run:5exception in Thread "Thread-0" java.util.ConcurrentModificationException at    java.util.arraylist$itr.checkforcomodification (Unknown Source)    At Java.util.arraylist$itr.next (Unknown Source) at    Test. Arraylisttest$threadone.run (arraylisttest.java:23)
ii. Causes of Fail-fast

With the above examples and explanations, I initially knew that the reason for the fail-fast is that when the program iterates over the collection, a thread modifies the collection on the structure, and the iterator throws Concurrentmodificationexception exception information, resulting in fail-fast.

To understand the fail-fast mechanism, we must first understand the concurrentmodificationexception anomaly. This exception is thrown when the method detects concurrent modifications to an object, but does not allow the modification. It is also important to note that the exception does not always indicate that the object has been modified concurrently by a different thread, and that if a single thread violates the rule, it is also possible to throw a change exception.

Admittedly, the fast failure behavior of the iterator cannot be guaranteed, it cannot guarantee that the error will occur, but the fast failure operation will do its best to throw the concurrentmodificationexception exception, so It is a bad practice to write a program that relies on this exception to improve the correctness of such operations: Concurrentmodificationexception should only be used to detect bugs. Below I will take ArrayList as an example to further analyze the cause of fail-fast.

From the front we know that Fail-fast is generated when the iterator is being manipulated. Now let's take a look at the source code of the iterator in ArrayList:

 private class Itr implements iterator<e> {int cursor;        int lastret =-1;        int expectedmodcount = ArrayList.this.modCount;        public Boolean Hasnext () {return (This.cursor! = ArrayList.this.size);            } public E Next () {checkforcomodification (); /** Omit here Code */} public void Remove () {if (This.lastret < 0) throw new illegals            Tateexception ();            Checkforcomodification (); /** omit the code here */} final void Checkforcomodification () {if (ArrayList.this.modCount = = This.expecte            Dmodcount) return;        throw new Concurrentmodificationexception (); }    }

From the source code above, we can see that the iterator calls the next (), remove () method is called the Checkforcomodification () method, the method is mainly to detect modcount = = Expectedmodcount? If unequal then throws Concurrentmodificationexception exception, thus produces fail-fast mechanism. So to figure out why there is a fail-fast mechanism we have to use to figure out why modcount! = Expectedmodcount, when their values change.

Expectedmodcount is defined in ITR: int expectedmodcount = ArrayList.this.modCount, so his value is impossible to modify, so the change is modcount. Modcount is defined in abstractlist as a global variable:

protected transient int modcount = 0;

So when did he change for what reason? Please see the source code of ArrayList:

    Public boolean Add (E ParamE) {ensurecapacityinternal (this.size + 1); /** omitted here Code */} private void ensurecapacityinternal (int paramint) {if (This.elementdata = = Empty_elementdata        ) Paramint = Math.max (ten, Paramint);    Ensureexplicitcapacity (Paramint);    } private void ensureexplicitcapacity (int paramint) {this.modcount + = 1;        Modify Modcount/** Omit here Code */} public boolean remove (Object paramobject) {int i;                    if (Paramobject = = null) for (i = 0; i < this.size; ++i) {if (this.elementdata[i]! = NULL)                Continue                Fastremove (i);            return true; } else for (i = 0; i < this.size; ++i) {if (!) (                Paramobject.equals (This.elementdata[i])) continue;                Fastremove (i);            return true;    } return false; } private void Fastremove (int paramint) {this.modcount + = 1;    Modify Modcount/** Omit here Code */} public void Clear () {this.modcount + = 1; Modify Modcount/** omit the code here */}

From the above source code, we can see that the ArrayList, regardless of the add, remove, clear method as long as it involves changing the number of ArrayList elements of the method will lead to modcount changes. So here we can make a preliminary judgment because the Expectedmodcount and modcount changes are not synchronized, resulting in the difference between the two thus producing fail-fast mechanism. Knowing the root cause of the generation of fail-fast, we can have the following scenarios:

There are two threads (thread A, thread B), where thread A is responsible for traversing list, thread B modifying list. Thread A is at some point in traversing the list process (at this point expectedmodcount = modcount=n), the thread starts, and thread B adds an element, which is the value of the Modcount change (modcount + 1 = N + 1). When thread a continues to traverse the Execute next method, the advertisement Checkforcomodification method discovers Expectedmodcount = N, and Modcount = n + 1, which varies, The concurrentmodificationexception exception is thrown and the fail-fast mechanism is generated.

So, until here we have fully understood the root cause of the fail-fast. Knowing the reason will find a solution.

Iii. Fail-fast Solutions

Through the previous examples, source code analysis, I think you have a basic understanding of the mechanism of fail-fast, the following I will produce a solution to the cause. There are two types of solutions:

Scenario One: in the traversal process all involved change modcount worthwhile place all plus synchronized or directly use Collections.synchronizedlist, so you can solve. However, it is not recommended because the synchronization lock caused by additions or deletions may block the traversal operation.

Scenario Two: use copyonwritearraylist to replace ArrayList. It is recommended to use this scenario.

What is Copyonwritearraylist? A thread-safe variant of ArrayList, where all mutable operations (add, set, and so on) are implemented by a new copy of the underlying array. This class produces more overhead, but in both cases it is well suited for use. 1: When you cannot or do not want to perform synchronous traversal, but you need to exclude conflicts from concurrent threads. 2: When the number of traversal operations greatly exceeds the number of variable operations. In both cases, it is appropriate to use copyonwritearraylist instead of ArrayList. So why Copyonwriterarraylist can replace ArrayList?

First, the copyonwriterarraylist, whether from the data structure, definition and ArrayList. Like ArrayList, it implements the list interface, which is implemented using arrays. The method also includes Add, remove, clear, iterator, and so on.

Second, Copyonwriterarraylist does not produce a concurrentmodificationexception exception at all, that is, it does not produce a fail-fast mechanism with iterators at all. Please see:

private static class Cowiterator<e> implements Listiterator<e> {        /** omitted here Code *        /Public E next () {            if (! ( Hasnext ()))                throw new Nosuchelementexception ();            Return this.snapshot[(this.cursor++)];        }        /** omit the code here */    }

The copyonwriterarraylist approach does not use the Checkforcomodification method in ArrayList to determine whether Expectedmodcount and Modcount are equal. Why would it do that, and why could it do so? Let's take the Add method as an example:

Public boolean Add (E ParamE) {        Reentrantlock localreentrantlock = this.lock;        Localreentrantlock.lock ();        try {            object[] ArrayOfObject1 = GetArray ();            int i = arrayofobject1.length;            object[] ArrayOfObject2 = arrays.copyof (ArrayOfObject1, i + 1);            Arrayofobject2[i] = ParamE;            SetArray (ARRAYOFOBJECT2);            int j = 1;            return j;        } finally {            localreentrantlock.unlock ();        }    }        final void SetArray (object[] paramarrayofobject) {        this.array = paramarrayofobject;    }

One of the biggest differences between the Add method of Copyonwriterarraylist and the Add method of ArrayList is the following three lines of code:

object[] ArrayOfObject2 = arrays.copyof (ArrayOfObject1, i + 1); Arrayofobject2[i] = Parame;setarray (ARRAYOFOBJECT2);

This is the three lines of code that make copyonwriterarraylist not throw concurrentmodificationexception exceptions. Their charm is to copy the original array, and then add on the copy array, which does not affect the array in the Cowiterator at all.

So the core concept represented by Copyonwriterarraylist is that any operation that changes the structure of an array (add, remove, clear, and so on) copyonwriterarraylist copy the existing data. Then modify the copy data, so that it does not affect the data in the Cowiterator, the modification after the completion of the original data to change the reference. At the same time, the cost is to produce a large number of objects, while the copy of the array is quite lossy.

Java fail-fast mechanism

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.