Oc-windows.ru

IT Новости из мира ПК
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Java util concurrentmodificationexception

Избавляемся от ConcurrentModificationException

Как известно, ConcurrentModificationException к многопоточности никакого отношения не имеет. Возникает эта гадость, когда мы пытаемся модифицировать коллекцию во время итерирования по ней. Как обычно, это имеет исторические корни: коллекции и итераторы появились в Java 1.2, в те времена избежать явного использования итератора при обходе коллекции было никак нельзя, так что предложение менять коллекцию посредством методов итератора не выглядело совсем ужасным:

Не, всё же выглядело. Но никаких других вариантов не было. Позже в пятой джаве появляется цикл foreach, и использование итераторов становится преимущественно неявным:

«Ишь чего захотели! Юзайте явные итераторы, дорогие кастомеры, и не выделывайтесь» — наверное что-то такое думали разработчики джава платформы работая над пятеркой.

В шестой джаве появляется пакет конкаренси. Теперь можно cделать так:

И получить set который не кидается ConcurrentModificationException-ами. Но опять же счастье не совсем полное:

  1. Oбычно многопоточность нам вовсе не нужна
  2. Не подерживаются null ни в качестве элементов, ни ключей, ни значений. Да и ладно, честно сказать.
  3. Порядок элементов не определён и может меняться — вот это гораздо хуже. Т.е. если мы бежим по элементам и ведём некий подсчёт с потерей точности, то нас могут поджидать неприятные сюрпризы и разные результаты на одних и тех же наборах данных, что, скажем, не всегда хорошо. Так же бывают задачи, где желательно сохранить именно изначальный порядок данных. Ну и вот такие штуки тоже имеют место быть:
Вывод

Вывод

Поэтому сейчас мы сделаем свою собственную коллекцию с чётким порядком. И так, что мы хотим получить:

  1. В рамках одного треда можно добавлять и удалять элементы в любой момент без всяких эксепшенов. И конечно же за константное время.
  2. Можно хранить null-ы, если вдруг хочется.
  3. Элементы обходятся в том порядке в котором были добавлены.

Всё это с легкостью достигается с помощью слегка доработанного двунаправленного списка:

  1. Удаляя элемент мы не будем обнулять ссылку на следующий, т. е. eсли итератор стоит на данном элементе, то он сможет пройти дальше.
  2. В конце списка поместим фэйковый элемент, который превращается в настоящий когда в список что-нибудь добавляют. Т.е. даже добравшись до конца списка итератор не упирается в null и может продолжить работу если в коллекции появляется новый элемент. Далее в коде этот фейковый элемент называется placeholder.

Посмотрим на картинку.

  1. В начале у нас есть элементы A, B, C, D.
  2. Затем элементы C и D удаляются.
  3. Добавляется новый элемент E.

Можно заметить, что если на момент удалений у нас был итератор указывавший на элемент С, то двигаясь дальше по ссылкам он доберется до вновь добавленного элемента E. Если же никакого итератора не было, то ничто не мешает сборщику мусора освободить память от удаленных элементов.

Ну и для константного времени доступа нам, очевидно, нужен хэшмап:

Теперь можно делать так:

Понятно, что аналогично можно сконструировать и LinkedMap. Вот в общем-то и всё, ещё один велосипед готов. Почему подобным образом не доработали библиотечные LinkedHashMap и LinkedHashSet? Кто знает, возможно чтобы джависты завидовали джаваскриптистам.

Avo >Last modified: May 7, 2019

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

In the 9 years of running Baeldung, I’ve never, ever done a «sale».
But. we’ve also not been through anything like this pandemic either.
And, if making my courses more affordable for a while is going to help a company stay in business, or a developer land a new job, make rent or be able to provide for their family — then it’s well worth doing.
Effective immediately, all Baeldung courses are 33% off their normal prices!
You’ll find all three courses in the menu, above, or here.

1. Introduction

In this article, we’ll take a look at the ConcurrentModificationException class.

First, we’ll give an explanation how it works, and then prove it by using a test for triggering it.

Finally, we’ll try out some workarounds by using practical examples.

Читать еще:  Javascript is defined

2. Triggering a ConcurrentModificationException

Essentially, the ConcurrentModificationException is used to fail-fast when something we are iterating on is modified. Let’s prove this with a simple test:

As we can see, before finishing our iteration we are removing an element. That’s what triggers the exception.

3. Solutions

Sometimes, we might actually want to remove elements from a collection whilst iterating. If this is the case, then there are some solutions.

3.1. Using an Iterator Directly

A for-each loop uses an Iterator behind the scenes but is less verbose. However, if we refactored our previous test to use an Iterator, we will have access to additional methods, such as remove(). Let’s try using this method to modify our list instead:

Now we will notice that there is no exception. The reason for this is that the remove() method does not cause a ConcurrentModificationException. It is safe to call while iterating.

3.2. Not Removing During Iteration

If we want to keep our for-each loop, then we can. It’s just that we need to wait until after iterating before we remove the elements. Let’s try this out by adding what we want to remove to a toRemove list as we iterate:

This is another effective way of getting around the problem.

3.3. Using removeIf()

Java 8 introduced the removeIf() method to the Collection interface. This means that if we are working with it, we can use ideas of functional programming to achieve the same results again:

This declarative style offers us the least amount of verbosity. However, depending on the use case, we may find other methods more convenient.

3.4. Filtering Using Streams

When diving into the world of functional/declarative programming, we can forget about mutating collections, instead, we can focus on elements that should be actually processed:

We’ve done the inverse to our previous example, by providing a predicate for determining elements to include, not exclude. The advantage is that we can chain together other functions alongside the removal. In the example, we use a functional map(), but could use even more operations if we want to.

4. Conclusion

In this article we’ve shown problems that you may encounter if you’re removing items from a collection whilst iterating, and also provided some solutions to negate the issue.

The implementation of these examples can be found over on GitHub. This is a Maven project, so should be easy to run as is.

java.util.ConcurrentModificationException

java.util.ConcurrentModificationException is a very common exception when working with Java collection classes. Java Collection classes are fail-fast, which means if the Collection will be changed while some thread is traversing over it using iterator, the iterator.next() will throw ConcurrentModificationException. Concurrent modification exception can come in case of multithreaded as well as a single threaded java programming environment.

Table of Contents

java.util.ConcurrentModificationException

Let’s see the concurrent modification exception scenario with an example.

Above program will throw java.util.ConcurrentModificationException when executed, as shown in below console logs.

From the output stack trace, its clear that the concurrent modification exception is coming when we call iterator next() function. If you are wondering how Iterator checks for the modification, its implementation is present in AbstractList class where an int variable modCount is defined. modCount provides the number of times list size has been changed. modCount value is used in every next() call to check for any modifications in a function checkForComodification().

Now comment out the list part and run the program again. You will see that there is no ConcurrentModificationException being thrown now.

Since we are updating the existing key value in the myMap, its size has not been changed and we are not getting ConcurrentModificationException. Note that the output may differ in your system because HashMap keyset is not ordered like a List. If you will uncomment the statement where I am adding a new key-value in the HashMap, it will cause ConcurrentModificationException.

Читать еще:  Код ошибки 0xc000007b

To Avoid ConcurrentModificationException in multi-threaded environment

  1. You can convert the list to an array and then iterate on the array. This approach works well for small or medium size list but if the list is large then it will affect the performance a lot.
  2. You can lock the list while iterating by putting it in a synchronized block. This approach is not recommended because it will cease the benefits of multithreading.
  3. If you are using JDK1.5 or higher then you can use ConcurrentHashMap and CopyOnWriteArrayList >To Avoid ConcurrentModificationException in single-threaded environment

You can use the iterator remove() function to remove the object from underlying collection object. But in this case, you can remove the same object and not any other object from the list.

Let us run an example using Concurrent Collection classes:

The output of the above program is shown below. You can see that there is no ConcurrentModificationException being thrown by the program.

From the above example it’s clear that:

  1. Concurrent Collection classes can be modified safely, they will not throw ConcurrentModificationException.
  2. In case of CopyOnWriteArrayList, iterator doesn’t accommodate the changes in the list and works on the original list.
  3. In case of ConcurrentHashMap, the behaviour is not always the same.

It is taking the new object added with key “4” but not the next added object with key “5”.

Now if I change the condition to below.

In this case, it’s not considering the newly added objects.

So if you are using ConcurrentHashMap then avo >Use for loop to avoid java.util.ConcurrentModificationException

If you are working on single-threaded environment and want your code to take care of the extra added objects in the list then you can do so using for loop rather than an Iterator.

Note that I am decreasing the counter because I am removing the same object, if you have to remove the next or further far object then you don’t need to decrease the counter. Try it yourself.

One More Thing: You will get ConcurrentModificationException if you will try to modify the structure of the original list with subList. Let’s see this with a simple example.

Output of above program is:

According to the ArrayList subList documentation, structural modifications is allowed only on the list returned by subList method. All methods on the returned list first check to see if the actual modCount of the backing list is equal to its expected value and throw a ConcurrentModificationException if it is not.

Java Exception Handling – ConcurrentModificationException

Today we’ll bite off another morsel from our delicious and mildly satiating Java Exception Handling series with a close look at the java.util.ConcurrentModificationException. The java.util.ConcurrentModificationException is typically thrown when code attempts to modify a data collection while that collection is actively in use, such as being iterated.

We’ll take the time in this article to further explore the java.util.ConcurrentModificationException , looking at where it sits in the Java Exception Hierarchy, along with some simple, functional code samples that will illustrate common scenarios that java.util.ConcurrentModificationExceptions might occur, and how to prevent them. Let’s get to it!

The Technical Rundown

  • All Java errors implement the java.lang.Throwable interface, or are extended from another inherited class therein.
  • java.lang.Exception inherits from java.lang.Throwable .
  • java.lang.RuntimeException inherits from java.lang.Exception .
  • Finally, java.util.ConcurrentModificationException inherits from java.lang.RuntimeException /

When Should You Use It?

As discussed in the introduction, the most typical scenario that might lead to a java.util.ConcurrentModificationException is when performing modifications on a collection object that is currently in use. To illustrate, in our example code we’ll be creating a basic ArrayList<> object, adding a few items to it, then iterating through the elements. Depending on how we build our application and, therefore, how we choose to perform any modifications during the iteration, we’ll see that the application will behave very differently.

To begin, let’s start with the full working code sample below. At the end of the sample block we’ll go through each section in more detail to see what’s going on:

To make our example a bit more realistic we’ve added a simple Book class with a few fields to store basic book information:

Our goal is to build a List collection of Books that we can iterate through, while simultaneously checking for a particular element in the list and proactively removing any matching book element from the list while the iteration continues. To accomplish this we start with the modifiedListExample() method:

As you can see, this method creates a new ArrayList object called library , adds a handful of Books to the list, then performs an advanced for loop on the library list. For each book element in the list we output the book info to the log, then also check if the current book element has a title of «Gone Girl» , in which case we remove that element from the library list. Let’s execute this method and see what we get in the log output:

Everything was working fine at first, but after removing «Gone Girl» from the library list we ran into a problem and threw a java.util.ConcurrentModificationException . The reason for this is that the JVM couldn’t determine what element should’ve come next in our for loop, since, for example, the original fourth element ( «His Dark Materials» ) is no longer where it should’ve been.

To fix this issue we can slightly modify how we perform our iteration of library elements and, instead of using an advanced for loop syntax as above, we can create a new Iterator object, which will serve as our means of iterating through elements and, most importantly, allow us to remove any matched book elements without affecting the iteration process.

Thus let’s take a look at the modifiedIteratorExample() method:

The big difference is the for loop using a new Iterator object, grabbed from the library.iterator() method. We then call the bookIterator.hasNext() method to check if another element exists, then retrieve it inside the loop with bookIterator.next() . Lastly, and most importantly, we are not calling library.remove() directly on the parent list object. Instead we use the bookIterator.remove() method, which allows us to safely remove the current element from the iterator, so the upcoming bookIterator.hasNext() and bookIterator.next() don’t fail and throw another java.util.ConcurrentModificationException .

The result is that our log output shows all books except the one we removed and skipped over:

It’s also worth noting that the above sample code is a bit verbose, just to better illustrate what’s going on with our iteration. We could almost entirely remove the for code block and use the following snippet instead, which uses a lambda expression to check each book element and perform a remove function if the title is what we’re filtering:

The Airbrake-Java library provides real-time error monitoring and automatic exception reporting for all your Java-based projects. Tight integration with Airbrake’s state of the art web dashboard ensures that Airbrake-Java gives you round-the-clock status updates on your application’s health and error rates. Airbrake-Java easily integrates with all the latest Java frameworks and platforms like Spring , Maven , log4j , Struts , Kotlin , Grails , Groovy , and many more. Plus, Airbrake-Java allows you to easily customize exception parameters and gives you full, configurable filter capabilities so you only gather the errors that matter most.

Check out all the amazing features Airbrake-Java has to offer and see for yourself why so many of the world’s best engineering teams are using Airbrake to revolutionize their exception handling practices!

Ссылка на основную публикацию
Adblock
detector