JUC (iii)--copyonwritearraylist, Copyonwritearrayset

Source: Internet
Author: User
Tags arrays data structures hash mutex static class volatile concurrentmodificationexception

I. Introduction of Copyonwritearraylist


It is equivalent to thread-safe ArrayList. Like ArrayList, it is a mutable array, but unlike ArrayList, it has the following characteristics:
1. It is best suited for applications that have the following characteristics: The List size is typically kept small, read-only operations are much larger than mutable operations , and there is a need to prevent conflicts between threads during traversal.
2. It is thread-safe.
3. Because the entire underlying array is usually copied, variable operations (add (), set (), and remove (), and so on , are expensive.
4. Iterators support Hasnext (), Next () and other immutable operations, but do not support variable remove ().
5. Iterating with iterators is fast and does not conflict with other threads. When you construct an iterator, the iterator relies on the invariant array snapshot.


Ii. copyonwritearraylist principles and data Structures


Description :
1. Copyonwritearraylist implements the list interface, so it is a queue.
2. Copyonwritearraylist contains the member lock. Each copyonwritearraylist is bound to a mutex lock, which enables mutually exclusive access to the Copyonwritearraylist through lock.
3. Copyonwritearraylist contains an array of member arrays, which shows that copyonwritearraylist is essentially implemented by an array.

The following is a further explanation of the copyonwritearraylist principle from the "Dynamic array" and "Thread safety" two.


1. Copyonwritearraylist's "dynamic array" mechanism --it has a "volatile array" (array) inside it to hold the data. When you add/modify/delete data, a new array is created, the updated data is copied to the newly created array, and the array is then assigned to the volatile array. That's why it's called copyonwritearraylist. Copyonwritearraylist is a dynamic array that is implemented in this way, but because it creates a new array in the "Add/Modify/delete" data, it involves the manipulation of modifying the data, copyonwritearraylist inefficient But it is more efficient to simply traverse the search.

2. Copyonwritearraylist's "thread-safe" mechanism -is achieved through volatile and mutex locks. The copyonwritearraylist is a "volatile array" to hold the data. When a thread reads a volatile array, it can always see the other thread's last write to the volatile variable, which provides the guarantee that the "read data is always up-to-date" mechanism is provided by volatile

The copyonwritearraylist protects the data by mutual exclusion locks. When adding/modifying/deleting data, the mutex is acquired first, then the data is updated to the "volatile array", and then the mutex is released, thus protecting the data is achieved.
Third, copyonwritearraylist source analysis


Below we analyze the principle of copyonwritearraylist from the 5 aspects of "Create, add, delete, Fetch, traverse".


1. Create

Public Copyonwritearraylist () {
    setarray (new object[0]);

Public copyonwritearraylist (collection<? extends e> c) {
    object[] elements = C.toarray ();
    if (Elements.getclass ()! = object[].class)
        elements = arrays.copyof (elements, Elements.length, object[].class);
    SetArray (elements);
}

Public Copyonwritearraylist (e[] tocopyin) {
    SetArray (arrays.copyof (Tocopyin, Tocopyin.length, Object[].class)) ;
}

All three constructors call the SetArray method:

Private volatile transient object[] array;

Final object[] GetArray () {
    return array;
}

final void SetArray (object[] a) {
    array = A;
}

Description :

The function of SetArray () is to assign a value to the array, where the array is a volatile transient object[] type, that is, the array is a "volatile array".

With regard to the volatile keyword, we know that "volatile can make a variable visible", that is, reading a volatile variable, always seeing (any thread) to the volatile variable

The last write. Because of this feature, every time a "volatile array" is updated, other threads can see the updates made to it.

About the transient keyword, it works in serialization, and the transient variable is not automatically serialized. Transient is not the focus of this article, you can understand.

For more information about transient, please refer to: http://blog.csdn.net/luotuomianyang/article/details/51915759
2. Add


Take Add (e) as an example to explain the addition of copyonwritearraylist. The following is the code for add (E E):


Public boolean Add (E e) {
    final reentrantlock lock = This.lock;
    Get "lock"
    lock.lock ();
    try {
        //Gets the data and data length in the original "volatile array".
        object[] elements = GetArray ();
        int len = elements.length;
        Creates a new array newelements and copies the original data to newelements;
        //newelements The length of the array = "Length of original array" +1
        object[] newelements = arrays.copyof (elements, Len + 1);
        Save the "newly added element" to newelements.
        Newelements[len] = e;
        Assigns a newelements to the "volatile array".
        SetArray (newelements);
        return true;
    } finally {
        //release "lock"
        lock.unlock ();
    }
}

Note : The role of Add (e) is to add data e to the "volatile array". It is implemented by creating a new array, then copying the data from the original "volatile array" into the new array, and adding the new data to the new array, and finally assigning a value to the "volatile array".
There are two points in Add (e E) that need attention.
First, the exclusive lock (lock) is obtained before the "add operation", and if a thread is required to acquire the lock at this point, it must wait for it, and when the operation is complete, the exclusive lock (lock) is released and the other thread acquires the lock. Exclusive locks prevent multiple threads from modifying data at the same time. Lock is defined as follows:

Transient final reentrantlock lock = new Reentrantlock ();

More about Reentrantlock, you can refer to: Java Multithreading Series--"Juc lock" 02 of the Mutual exclusion lock Reentrantlock
Second, when the operation is complete, the "volatile array" is updated with SetArray (). And, as we mentioned earlier, "That is, reading a volatile variable, you can always see (any thread) the last write to this volatile variable," so that each time the element is added, other threads will see the newly added element.


3. Get

Take the Get (int index) as an example to describe the delete operation for Copyonwritearraylist. The following is the code for Get (int index):

Public E get (int index) {
    return get (GetArray (), index);
}

Private E get (object[] A, int index) {
    return (E) a[index];
}

Description: The implementation of Get (int index) is simple, which is to return the index element in the "volatile array".


4. Delete

Take remove (int index) as an example to explain the copyonwritearraylist delete operation. The following is the code for remove (int index):

Public E Remove (int index) {
    final reentrantlock lock = This.lock;
    Get "lock"
    lock.lock ();
    try {
        //Gets the data and data length in the original "volatile array".
        object[] elements = GetArray ();
        int len = elements.length;
        Gets the index data in the elements array.
        E oldValue = Get (elements, index);
        int nummoved = len-index-1;
        If the last element is deleted, it is processed directly from arrays.copyof () without the need to create a new array.
        ///Otherwise, create a new array, and then copy the "elements other than the deleted element in the volatile array" to the new array, and finally assign the new array to the "volatile array".
        if (nummoved = = 0)
            setarray (arrays.copyof (elements, len-1));
        else {
            object[] newelements = new object[len-1];
            System.arraycopy (elements, 0, newelements, 0, index);
            System.arraycopy (elements, index + 1, newelements, index,
                             nummoved);
            SetArray (newelements);
        }
        return oldValue;
    } finally {
        //release "lock"
        lock.unlock ();
    }
}

Note : The function of remove (int index) is to delete the index element in the "volatile array". It is implemented in a way that, if the last element is deleted, it is processed directly through arrays.copyof () without the need to create a new array. Otherwise, the new array is then copied to the new array with elements other than the deleted element in the volatile array, and finally the new array is assigned to the "volatile array".
As with Add (e e), remove (int index) is also "acquires an exclusive lock before operation, and after the operation completes, releases the exclusive is", and "when the operation completes, it is updated to the volatile array". 5. Traverse

Take iterator () as an example to illustrate the copyonwritearraylist traversal operation. Here is the code for iterator ():

Public iterator<e> Iterator () {
    return new cowiterator<e> (GetArray (), 0);
}

Description : Iterator () returns the Cowiterator object.

Cowiterator implementation of the Listiterator interface, its source code is as follows:


private static class Cowiterator<e> implements Listiterator<e> {private final object[] snapshot;

    private int cursor;
        Private Cowiterator (object[] elements, int initialcursor) {cursor = Initialcursor;
    snapshot = elements;
    } public boolean Hasnext () {return cursor < snapshot.length;
    } public boolean hasprevious () {return cursor > 0; }//Gets the next element @SuppressWarnings ("unchecked") public E Next () {if (! Hasnext ()) throw new
        Nosuchelementexception ();
    Return (E) snapshot[cursor++]; }//Gets the previous element @SuppressWarnings ("unchecked") public E Previous () {if (! hasprevious ()) th
        Row 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 (); }
}

Description : Cowiterator does not support modifying an element's operation. For example, Cowiterator throws an exception for operations such as remove (), set (), and add ().
Also, one thing to mention is that the copyonwritearraylist return iterator does not throw an concurrentmodificationexception exception, that is, it is not a fail-fast mechanism.
About the fail-fast mechanism, you can refer to the "Java Collection series 04 Fail-fast summary (through ArrayList to illustrate Fail-fast principles, solutions)."
Four. An example


Import java.util.*;

Import java.util.concurrent.*;
 /* * Copyonwritearraylist is a "thread safe" dynamic array, while ArrayList is non-thread safe.
 * * Below is an example of "multiple threads working simultaneously and traversing list" * (01) The program works correctly when list is a Copyonwritearraylist object.
 * (02) When the list is a ArrayList object, the program generates an Concurrentmodificationexception exception.
    * * @author Skywang */public class CopyOnWriteArrayListTest1 {///Todo:list is a ArrayList object, the program will error.
    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 at the same time to manipulate the list.
        New MyThread ("Ta"). Start ();
    New MyThread ("TB"). Start ();
        } private static void Printall () {String value = null;
        Iterator iter = List.iterator ();
            while (Iter.hasnext ()) {value = (String) iter.next ();
        System.out.print (value+ ",");
    } System.out.println (); } private static Class MyThread ExteNDS Thread {MyThread (String name) {super (name);
            } @Override public void Run () {int i = 0; while (i++ < 6) {//"thread name" + "-" + "ordinal" String val = Thread.CurrentThread (). GetName () + "
                -"+i;
                List.add (Val);
                Traverse the list through "Iterator".
            Printall (); }
        }
    }
}

A running result

Ta-1, Tb-1, Ta-1, 
tb-1, 
ta-1, Ta-1, Tb-1, Tb-1, Tb-2, 
tb-2, Ta-1, Ta-2, 
tb-1, Ta-1, Tb-2, Tb-1, Ta-2, tb-2 , tb-3, 
ta-2, Ta-1, tb-3, Tb-1, ta-3, 
tb-2, Ta-1, Ta-2, Tb-1, tb-3, Tb-2, ta-3, Ta-2, tb-4, 
tb-3, Ta-1, ta-3, Tb-1, Tb-4, Tb-2, ta-4, Ta-2, 
ta-1, tb-3, Tb-1, ta-3, Tb-2, tb-4, Ta-2, ta-4, tb-3, tb-5, ta-3, Ta-1, tb-4, Tb-1, 
Ta-4, Tb-2, tb-5, Ta-2, ta-5, 
tb-3, Ta-1, ta-3, Tb-1, tb-4, Tb-2, ta-4, Ta-2, tb-5, tb-3, ta-5, ta-3, tb-6, tb-4, 
Ta-4, Tb-5, ta-5, tb-6, ta-6,

The result shows that if the list in the source code is changed to a ArrayList object, the program generates a Concurrentmodificationexception exception.


Q: I don't quite understand why we need to create a new array when add, and we can't manipulate the volatile array directly. Lock has guaranteed that there will be no write conflicts, volatile also guarantees that add a record to the volatile array, other lines Cheng Lima visible, then why do you need to create a new array, and then copy to the volatile array, this is not a few times?

A:rraylis the ensurecapacityinternal (size + 1) method is called before each addition to determine if the length of the array is exceeded, and the super-expansion is necessary. and copyonwritearraylist each expansion is only larger than the original to fill the new value, not like ArrayList each time the expansion of 3/2, so that every time add is sure to create a new array, the capacity is 1 larger than the original. In fact, if the direct change, then read the time can get the new add an element, but perhaps there is no set value in, cow read is not locked, add operation is not atomic.


Transferred from: http://www.cnblogs.com/skywang12345/p/3498483.html


Wu, Copyonwritearrayset

It is a thread-safe, unordered collection that can be understood as thread-safe hashset. Interestingly, although both Copyonwritearrayset and hashset inherit from the common parent class Abstractset, HashSet is implemented through the hash list (HASHMAP). Copyonwritearrayset is implemented by "dynamic Array (copyonwritearraylist)" and is not a hash table.
Similar to Copyonwritearraylist, Copyonwritearrayset has the following characteristics:
1. It is best suited for applications that have the following characteristics: The Set size is usually kept small, read-only operations are much more than mutable, and there is a need to prevent conflicts between threads during traversal.
2. It is thread-safe.
3. Because the entire underlying array is usually copied, variable operations (add (), set (), and remove (), and so on, are expensive.
4. Iterators support Hasnext (), Next () and other immutable operations, but do not support variable remove ().
5. Iterating with iterators is fast and does not conflict with other threads. When you construct an iterator, the iterator relies on the invariant array snapshot.

Description :
1. Copyonwritearrayset inherits from Abstractset, which means it is a collection.
2. Copyonwritearrayset contains the Copyonwritearraylist object, which is implemented through Copyonwritearraylist. And the Copyonwritearraylist essence is a dynamic array queue,
So Copyonwritearrayset is equivalent to a "collection" implemented through a dynamic array. Duplicate elements are allowed in copyonwritearraylist; however, Copyonwritearrayset is a collection, so it cannot have duplicate collections. As a result, Copyonwritearraylist provides additional APIs for the addition of Addifabsent () and Addallabsent (), which add elements through these APIs, and perform the add operation only if the element does not exist.
As for Copyonwritearrayset, the "thread-safe" mechanism, like copyonwritearraylist, is achieved through volatile and mutex locks. This is explained in the previous section when describing the data structure of copyonwritearraylist, which is not repeated here.


Copyonwritearrayset is implemented through Copyonwritearraylist, and its APIs are basically implemented by invoking the Copyonwritearraylist API. believe that to copyonwritearraylist understand, the understanding of Copyonwritearrayset is the matter of the inevitable, so, here is no longer a detailed analysis of the Copyonwritearrayset code

Related Article

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.