🎓 Do your best and obey the destiny. The blogger is studying for a master’s degree in Southeast University. He loves fitness and basketball, and is willing to share what he sees and gains related to technology. He pays close attention to the public account @flying Veal, and gets the update of the article as soon as possible

🎁 This article has been included in the “CS-Wiki” Gitee official recommended project, has accumulated 1.5K + STAR, is committed to creating a perfect back-end knowledge system, in the road of technology to avoid detours, welcome friends to come to exchange and study

🍉 If you do not have a good project, you can refer to a project I wrote “Open source community system Echo” Gitee official recommended project, so far has accumulated 600+ star, SpringBoot + MyBatis + Redis + Kafka + Elasticsearch + Spring Security +… And provide detailed development documents and supporting tutorials. Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo


ConcurrentHashMap ConcurrentHashMap ConcurrentHashMap ConcurrentHashMap ConcurrentHashMap ConcurrentHashMap

OK, continue the previous article HashMap this set of eight, can not back a ten times? The final question is:

1. How to make HashMap thread safe?

There are generally three ways to replace native thread-unsafe Hashmaps:

1) Use the synchronizedMap method of the java.util.Collections class to wrap the HashMap to obtain a thread-safe HashMap by adding synchronized to all modifications. The method is as follows:

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 
Copy the code

2) Use a thread-safe Hashtable class instead, which locks all data operations (i.e. Synchronized)

3) Use the thread-safe ConcurrentHashMap class instead, which is different in JDK 1.7 and JDK 1.8. JDK 1.7 uses array + linked list to store data, and uses Segment locking to ensure thread-safe; JDK 1.8 uses arrays + linked lists/red-black trees to store data, and CAS + synchronized to ensure thread safety.

However, the first two threads have low concurrency and are prone to large-scale blocking. Therefore, ConcurrentHashMap is generally used and its performance and efficiency are significantly higher than those of the first two threads.

2. How is synchronizedMap thread safe?

This question should be easy to be missed, it does not often appear in the book, there is no good to ask. But for the sake of completeness, let me explain it here.

The synchronizedMap method is used to create a thread-safe Map:

Map m = Collections.synchronizedMap(newHashMap(...) );Copy the code

The static method in Collections, synchronizedMap, is an object that creates an inner class, which is synchronizedMap. It maintains a normal Map object and mutex inside, as shown below:

As you can see, SynchronizedMap has two constructors. If you pass in the mutex argument, use our own mutex. If none is passed in, the mutex is assigned to this, which means that the object on which the constructor was called is the mutex, called Map.

SynchronizedMap (SynchronizedMap); SynchronizedMap (SynchronizedMap); SynchronizedMap (SynchronizedMap);

Because multiple threads share the same mutex, only one thread can read or write at the same time, while other threads can only wait. Therefore, although it supports high concurrency, the concurrency is too low, and the performance is low in the case of multi-threading.

In addition, in most business scenarios, read operations between multiple threads do not conflict, so SynchronizedMap greatly limits read performance.

So SynchronizedMap is rarely used for multi-threaded concurrent scenarios.

3. What about hashtables?

Like SynchronizedMap, Hashtable imposes a pessimistic synchronized lock on every method. Let’s look at a few methods:

4. Besides this, is there any difference between Hashtable and HashMap?

A Hashtable does not allow a null key or value. A HashMap can have a null key or value.

Hashtable does not support null keys and values.

If we put a value of null into the Map, the Hashtable will throw a null pointer exception directly:

2) If we put a null key into the Map, the program will throw a null pointer exception when it executes the line shown in the box below. Because the key is null, we will call the method with a null value:

HashMap supports null keys and null values.

If the key we put in is null, HashMap returns 0 when it evaluates the hash value of the key:

That is, key-value pairs in a HashMap whose key is null have a hash of 0. Therefore, only one key-value pair with a null key will be stored in a HashMap object because they all have the same hash value.

2) If the value we put is null, a HashMap object can store multiple null key-value pairs since the PUT method does not verify that the value is null:

There is, however, a catch. Let’s look at the HashMap get method:

If the Map does not find a key-value pair for the key, the GET method returns a NULL object. Get (key) {get(key); get(key) {get(key);

  • HashMapThere is no key-value pair for this key in
  • HashMapThe value corresponding to the key is NULL

Therefore, in general we cannot use the get method to determine the presence of a key in a HashMap. Instead, we should use the containsKey method.

5. Why on earth does Hashtable not allow null keys and values? Why is that?

Not only is Hashtable not allowed to have a null key or value, but ConcurrentHashMap is also not allowed. As containers that support concurrency, they can be problematic in multithreaded environments if, like hashMaps, they allow null keys and null values.

Assuming they allow null keys and null values, let’s see what happens: when you get a value from a get(key), you can’t tell if the key actually exists if the result is null. So to do that, we need to call containsKey to determine whether this key is value = null or whether it doesn’t exist at all, and if it returns true, OK, Then we can call map.get(key) to get the value.

This logic is of course fine for a single-threaded HashMap. In a single thread, we can use the map.containskey (key) method to distinguish ambiguity when the value we get is null.

But! Since Hashtable and ConcurrentHashMap are multithreaded containers, the map object may already be different by the time map.get(key) is called.

Let’s say some thread A calls map.get(key) and it returns value = null because the key doesn’t exist. Of course, thread A will continue to use map.containsKey(key), which we expect to return false.

However, after thread A calls the map.get(key) method and before the map.containskey method, another thread B performs the map.put(key,null) operation. The map.containsKey method called by thread A returns true. This is not consistent with the reality of our hypothesis.

Therefore, for concurrency security, Hashtable and ConcurrentHashMap do not allow null keys and values.

6. What are the differences between Hashtable and HashMap?

In addition to the fact that Hashtable does not allow NULL keys and null values whereas HashMap does, there are several differences between the two:

1) Different initial capacities: The initial capacities of HashMap and Hashtable are 16 and 11 respectively. The default load factor for both is 0.75;

2) Different capacity expansion mechanisms: When the existing capacity is greater than the total capacity * load factor, the HashMap capacity expansion rule is double the current capacity, and the Hashtable capacity expansion rule is double the current capacity + 1.

First, Hashtable and HashMap have the same Iterator, Iterator.

Iterator iterator = map.keySet().iterator();
Copy the code

The Iterator of a HashMap is fail-fast, and the Iterator of a natural Hashtable is fail-fast. It is clear that Hashtable is fail-fast. The official JDK 1.8 documentation states:

But!! Hashtable has another iterator, Enumeration, that is fail-safe. Many bloggers on the web refer to Hashtable as fail-safe, which is incorrect and ambiguous!

7. This section describes the Fail-safe and Fail-fast mechanisms

Fail-safe and Fail-fast are systems design concepts that are not unique to Java collections. If you are familiar with Dubbo, you will remember that they are also part of Dubbo’s cluster fault-tolerant strategy.

Of course, the details of how these two mechanisms behave in Java collections and Dubbo are certainly different, and this article will focus on the details of how these two mechanisms behave in Java collections.

1) Fail-fast: a mechanism for quickly discovering system failures. If an exception occurs, stop the current operation immediately and report the fault to the upper-layer system.

Take the simplest example of fail-fast:

The advantage of this is that some error cases can be identified in advance, on the one hand, to avoid the execution of complex other code, on the other hand, the exception can be identified to do some specific processing.

The java.util package contains fail-fast collection classes, such as HashMap and HashTable.

The iterators returned by all of this class’s “collection view methods” are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

General meaning when the Iterator the Iterator was created, in addition to the methods to remove the Iterator itself can change the structure of the collection, if other factors change the structure of the collection, which will throw ConcurrentModificationException.

Structural changes, inserting or deleting elements in a collection is a structural change, but modifying an element in a collection is not a structural change. Let’s use Hashtable to illustrate an example of a fail-fast exception:

Parsing this code: On the first iteration, we removed the element in the set key = “a”, the structure of the set was changed, so the second iteration of the iterator will throw an exception.

Also, as a bonus, a for-each enhancement loop will throw an exception. For-each is essentially dependent on Iterator.

OK, let’s move on to the official document:

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

The fail-fast behavior of iterators is not 100% guaranteed. But fail – fast iterator will do best to throw ConcurrentModificationException. Therefore, it is not right for programmers to write programs that rely on this exception. The fail-fast behavior of iterators should only be used to detect bugs in programs.

2) Fail-safe: The system continues to run after a fault occurs.

As the name implies, in contrast to Fail-Fast, the Fail-safe mechanism does not throw exceptions when we make changes to the structure of the collection.

Containers under the java.util.concurrent package are fail-safe, such as ConcurrentHashMap, and can be used and modified concurrently in multiple threads. You can also add/remove in the for-each enhancement loop.

The only exception to this rule is java.util.Hashtable, which has another iterator, Enumeration, that is fail-safe.

There is a keys method that returns an Enumeration iterator:

As for why Fail-Safe does not throw exceptions, it is because, when the structure of the collection is changed, the Fail-safe mechanism makes a copy of the original collection and iterates over that copy of data. Therefore, while fail-safe does not throw exceptions, it has the following disadvantages:

  • There is no guarantee that the traversal is up to date. That is, the iterator iterates over the copy of the collection it gets at the beginning of the iteration. The iterator does not know the changes that occurred in the original collection during the iteration.
  • Replication requires additional space and time overhead.

8. Tell me how Fail-fast works

We can find from the source code that when iterators execute next() and other methods, they will call checkForComodification method to check whether modCount and expectedModCount are equal. If they are not, exceptions will be thrown to terminate the traversal. If it is equal, it returns traversal.

The value expectedModcount is given a fixed value when the object is created, which means the expectedModcount is not changing, But modCount changes when we change the number of elements in the collection (delete, insert). If the iterator finds that modCount has changed the next time it traverses the element, it will throw an exception when it reaches the statement.

| flying veal 🎉 pay close attention to the public, get updates immediately

  • The blogger is a master student in Southeast University. In her spare time, she runs a public account “Flying Veal”, which was opened on December 29, 2020/29. Focus on sharing computer basics (data structure + algorithm + computer network + database + operating system + Linux), Java basics and interview guide related original technical good articles. The purpose of this public account is to let you can quickly grasp the key knowledge, targeted. I hope you can support me and grow with veal 😃
  • And recommend personal maintenance of open source tutorial project: CS-Wiki (Gitee recommended project, has accumulated 1.5K + STAR), is committed to creating a perfect back-end knowledge system, in the road of technology to avoid detours, welcome friends to come to exchange learning ~ 😊
  • If you don’t have any outstanding projects, you can refer to the Gitee official recommended project of “Open Source Community System Echo” written by me, which has accumulated 600+ star so far. SpringBoot + MyBatis + Redis + Kafka + Elasticsearch + Spring Security +… And provide detailed development documents and supporting tutorials. Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo