Table of Contents
What is a CopyOnWrite container?
The implementation principle of CopyOnWriteArrayList
Application scenarios of CopyOnWrite
Disadvantages of CopyOnWrite
Home Java javaTutorial Java concurrent programming: Implementation principle of concurrent container CopyOnWriteArrayList

Java concurrent programming: Implementation principle of concurrent container CopyOnWriteArrayList

Jul 30, 2018 am 11:41 AM
Multithreading concurrent

 Copy-On-Write, referred to as COW, is an optimization strategy used in programming. The basic idea is that everyone is sharing the same content from the beginning. When someone wants to modify the content, they will actually copy the content to form a new content and then modify it. This is a kind of delayed laziness. Strategy. Starting from JDK1.5, the Java concurrency package provides two concurrent containers implemented using the CopyOnWrite mechanism, which are CopyOnWriteArrayList and CopyOnWriteArraySet. The CopyOnWrite container is very useful and can be used in many concurrent scenarios.

What is a CopyOnWrite container?

A CopyOnWrite container is a container that is copied when writing. The popular understanding is that when we add elements to a container, we do not add them directly to the current container, but first copy the current container to create a new container, and then add elements to the new container. After adding the elements, Then point the reference of the original container to the new container. The advantage of this is that we can perform concurrent reads on the CopyOnWrite container without locking, because the current container will not add any elements. Therefore, the CopyOnWrite container is also an idea of ​​separation of reading and writing, and reading and writing are different containers.

The implementation principle of CopyOnWriteArrayList

Before using CopyOnWriteArrayList, we first read its source code to understand how it is implemented. The following code is the implementation of the add method in CopyOnWriteArrayList (adding elements to CopyOnWriteArrayList). You can find that you need to lock when adding, otherwise N copies will be copied when writing with multiple threads.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/**

     * Appends the specified element to the end of this list.

     *

     * @param e element to be appended to this list

     * @return <tt>true</tt> (as specified by {@link Collection#add})

     */

public boolean add(E e) {

final ReentrantLock lock = this.lock;

lock.lock();

try {

         Object[] elements = getArray();

        int len = elements.length;

        Object[] newElements = Arrays.copyOf(elements, len 1);

        newElements[len] = e;

        setArray(newElements);

        return true;

} finally {

        lock.unlock();

}

}

There is no need to lock when reading. If multiple threads are adding data to CopyOnWriteArrayList when reading, the reading will still read the old data because there will be no locking when writing. Live the old CopyOnWriteArrayList.

1

2

3

public E get(int index) {

return get(getArray(), index);

}

There is no CopyOnWriteMap provided in JDK. We can refer to CopyOnWriteArrayList to implement one. The basic code is as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

twenty one

twenty two

twenty three

twenty four

25

26

27

28

29

30

31

32

33

import java.util.Collection;

import java.util.Map;

import java.util.Set;

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {

private volatile Map<K, V> internalMap;

public CopyOnWriteMap() {

       internalMap = new HashMap<K, V>();

}

public V put(K key, V value) {

        synchronized (this) {

        Map<K, V> newMap = new HashMap<K, V>(internalMap);

             V val = newMap.put(key, value);

            internalMap = newMap;

           return val;

        }

}

public V get(Object key) {

        return internalMap.get(key);

}

public void putAll(Map<? extends K, ? extends V> newData) {

        synchronized (this) {

        Map<K, V> newMap = new HashMap<K, V>(internalMap);

            newMap.putAll(newData);

            internalMap = newMap;

        }

}

}

The implementation is very simple. As long as we understand the CopyOnWrite mechanism, we can implement various CopyOnWrite containers and use them in different application scenarios.

Application scenarios of CopyOnWrite

The CopyOnWrite concurrent container is used in concurrent scenarios with more reading and less writing. For example, whitelist, blacklist, and product category access and update scenarios. If we have a search website, the user enters keywords to search for content in the search box of this website, but some keywords are not allowed to be searched. These keywords that cannot be searched will be placed in a blacklist, which is updated every night. When the user searches, it will check whether the current keyword is in the blacklist. If it is, it will prompt that the search cannot be performed. The implementation code is as follows:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

twenty one

twenty two

twenty three

twenty four

25

26

27

28

29

30

31

32

33

34

35

package com.ifeve.book;

import java.util.Map;

import com.ifeve.book.forkjoin.CopyOnWriteMap;

/**

* Blacklist Service

*

* @author fangtengfei

*

*/

public class BlackListServiceImpl {

private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String , Boolean>(

             1000);

public static boolean isBlackList(String id) {

       return blackListMap.get(id) == null ? false : true;

}

public static void addBlackList(String id) {

        blackListMap.put(id, Boolean.TRUE);

}

/**

* Add blacklist in batches

*

* @param ids

      */

public static void addBlackList(Map<String,Boolean> ids) {

        blackListMap.putAll(ids);

}

}

The code is very simple, but there are two things you need to pay attention to when using CopyOnWriteMap:

1. Reduce expansion overhead. Initialize the size of CopyOnWriteMap according to actual needs to avoid the overhead of CopyOnWriteMap expansion during writing.

 2. Use batch addition. Because each time you add, the container will be copied every time, so reducing the number of additions can reduce the number of times the container is copied. For example, use the addBlackList method in the above code.

Disadvantages of CopyOnWrite

The CopyOnWrite container has many advantages, but there are also two problems, namely memory usage and data consistency. So you need to pay attention to it when developing.

Memory usage problem. Because of the copy-on-write mechanism of CopyOnWrite, when a write operation is performed, two objects will reside in the memory at the same time, the old object and the newly written object (note: during copying, only the references in the container are copied. Only when writing, new objects will be created and added to the new container, while the objects in the old container are still in use, so there are two copies of object memory). If the memory occupied by these objects is relatively large, for example, about 200M, then writing 100M of data into it will occupy 300M of memory, which may cause frequent Yong GC and Full GC at this time. Previously, we used a service in our system that used the CopyOnWrite mechanism to update large objects every night, resulting in a Full GC of 15 seconds every night, and the application response time also became longer.

In view of the memory usage problem, you can reduce the memory consumption of large objects by compressing the elements in the container. For example, if the elements are all decimal numbers, you can consider compressing them into hexadecimal or hexadecimal numbers. Base 64. Or don't use the CopyOnWrite container, but use other concurrent containers, such as ConcurrentHashMap.

Data consistency issue. The CopyOnWrite container can only guarantee the final consistency of the data, but cannot guarantee the real-time consistency of the data. So if you want the written data to be read immediately, please do not use the CopyOnWrite container.

Related articles:

Java Concurrent Programming: CountDownLatch, CyclicBarrier and Semaphore

[JAVA Concurrent Programming Practice] Lock Sequence Deadlock

Related videos:

Java multi-threading and concurrency library advanced application video tutorial

The above is the detailed content of Java concurrent programming: Implementation principle of concurrent container CopyOnWriteArrayList. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: How To Unlock Everything In MyRise
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

C++ function exceptions and multithreading: error handling in concurrent environments C++ function exceptions and multithreading: error handling in concurrent environments May 04, 2024 pm 04:42 PM

Function exception handling in C++ is particularly important for multi-threaded environments to ensure thread safety and data integrity. The try-catch statement allows you to catch and handle specific types of exceptions when they occur to prevent program crashes or data corruption.

Application of concurrency and coroutines in Golang API design Application of concurrency and coroutines in Golang API design May 07, 2024 pm 06:51 PM

Concurrency and coroutines are used in GoAPI design for: High-performance processing: Processing multiple requests simultaneously to improve performance. Asynchronous processing: Use coroutines to process tasks (such as sending emails) asynchronously, releasing the main thread. Stream processing: Use coroutines to efficiently process data streams (such as database reads).

How to implement multi-threading in PHP? How to implement multi-threading in PHP? May 06, 2024 pm 09:54 PM

PHP multithreading refers to running multiple tasks simultaneously in one process, which is achieved by creating independently running threads. You can use the Pthreads extension in PHP to simulate multi-threading behavior. After installation, you can use the Thread class to create and start threads. For example, when processing a large amount of data, the data can be divided into multiple blocks and a corresponding number of threads can be created for simultaneous processing to improve efficiency.

How to deal with shared resources in multi-threading in C++? How to deal with shared resources in multi-threading in C++? Jun 03, 2024 am 10:28 AM

Mutexes are used in C++ to handle multi-threaded shared resources: create mutexes through std::mutex. Use mtx.lock() to obtain a mutex and provide exclusive access to shared resources. Use mtx.unlock() to release the mutex.

Challenges and countermeasures of C++ memory management in multi-threaded environment? Challenges and countermeasures of C++ memory management in multi-threaded environment? Jun 05, 2024 pm 01:08 PM

In a multi-threaded environment, C++ memory management faces the following challenges: data races, deadlocks, and memory leaks. Countermeasures include: 1. Use synchronization mechanisms, such as mutexes and atomic variables; 2. Use lock-free data structures; 3. Use smart pointers; 4. (Optional) implement garbage collection.

Challenges and strategies for testing multi-threaded programs in C++ Challenges and strategies for testing multi-threaded programs in C++ May 31, 2024 pm 06:34 PM

Multi-threaded program testing faces challenges such as non-repeatability, concurrency errors, deadlocks, and lack of visibility. Strategies include: Unit testing: Write unit tests for each thread to verify thread behavior. Multi-threaded simulation: Use a simulation framework to test your program with control over thread scheduling. Data race detection: Use tools to find potential data races, such as valgrind. Debugging: Use a debugger (such as gdb) to examine the runtime program status and find the source of the data race.

A guide to unit testing Go concurrent functions A guide to unit testing Go concurrent functions May 03, 2024 am 10:54 AM

Unit testing concurrent functions is critical as this helps ensure their correct behavior in a concurrent environment. Fundamental principles such as mutual exclusion, synchronization, and isolation must be considered when testing concurrent functions. Concurrent functions can be unit tested by simulating, testing race conditions, and verifying results.

Debugging and Troubleshooting Techniques in C++ Multithreaded Programming Debugging and Troubleshooting Techniques in C++ Multithreaded Programming Jun 03, 2024 pm 01:35 PM

Debugging techniques for C++ multi-threaded programming include using a data race analyzer to detect read and write conflicts and using synchronization mechanisms (such as mutex locks) to resolve them. Use thread debugging tools to detect deadlocks and resolve them by avoiding nested locks and using deadlock detection mechanisms. Use the Data Race Analyzer to detect data races and resolve them by moving write operations into critical sections or using atomic operations. Use performance analysis tools to measure context switch frequency and resolve excessive overhead by reducing the number of threads, using thread pools, and offloading tasks.

See all articles