ArrayList tirar `ConcurrentModificationException` al intentar ejecutar` .size) método ( `

stevendesu:

Actualizar

Como fue señalado por Jiri Tousek, el error que se estaba tirado en mi código ha engañado a muchos aficionados (y experimentado) los desarrolladores de Java. Contrariamente a lo que su nombre parece implicar, ConcurrentModificationExceptionno , no tiene nada que ver con multi-threading. Considere el siguiente código:

import java.util.List;
import java.util.ArrayList;

class Main {
  public static void main(String[] args) {
    List<String> originalArray = new ArrayList<>();
    originalArray.add("foo");
    originalArray.add("bar");
    List<String> arraySlice = originalArray.subList(0, 1);
    originalArray.remove(0);
    System.out.println(Integer.toString(arraySlice.size()));
  }
}

Esto lanzará una ConcurrentModificationExceptionpesar de no ser ninguna de enhebrado involucrados.

El nombre de la excepción engañosa me llevó a pensar que mi problema era el resultado de cómo estaba manejando multi-threading. He actualizado el título de mi post con la actual edición.

Original (título:? ¿Cómo informar de Java que haya terminado de modificar un ArrayList en un hilo)

Tengo el código que se ve más o menos como la siguiente:

class MessageQueue {
    private List<String> messages = new ArrayList<>();
    private List<String> messagesInFlight = new ArrayList<>();

    public void add(String message) {
        messages.add(message);
    }

    public void send() {
        if (messagesInFlight.size() > 0) {
            // Wait for previous request to finish
            return;
        }

        messagesInFlight = messages.subList(0, Math.min(messages.size, 10));
        for( int i = 0; i < messagesInFlight.size(); i++ )
        {
            messages.remove(0);
        }

        sendViaHTTP(messagesInFlight, new Callback() {
            @Override
            public void run() {
                messagesInFlight.clear();
            }
        });
    }
}

Esto se utiliza en mi código para fines de análisis. Cada 10 segundos me llaman messageQueue.send()desde un temporizador, y cada vez que se produce un evento de interés que llaman messageQueue.add(). Estos trabajos de clase * en su mayor parte * - Me pueden añadir mensajes y se envían a través de HTTP, y cuando la petición HTTP completa la devolución de llamada es un negocio

El problema se encuentra en la segunda señal del temporizador. Cuando llegué a la línea if (messagesInFlight.size() > 0) {, me sale el siguiente error:

java.util.ConcurrentModificationException
        at java.util.ArrayList$SubList.size(ArrayList.java:1057)

Parece como si no pudiera leer el .size()de la matriz en un hilo (devolución de llamada del segundo temporizador), ya que piensa la matriz todavía está siendo modificada por el otro hilo (devolución de llamada del primer contador de tiempo). Sin embargo, yo esperaría que el hilo de la primera temporizador fue destruida y limpiado después de mi llamada a sendViaHTTP, ya que no había ningún código adicional para que se ejecute. Además, la solicitud HTTP está completando a menos de 500 milisegundos, por lo que un total de 9,5 segundos pasa sin nada de tocar el vacío messagesInFlightarray

¿Hay una manera de decir por ejemplo "Hey, he terminado la modificación de esta matriz, la gente puede leer de manera segura ahora"? O tal vez una mejor manera de organizar mi código?

Jiri Tousek:

El problema más evidente que tienes ahí es que está utilizando ArrayList.subList(), mientras que no parecen entender lo que realmente hace:

Devuelve una vista de la parte de esta lista ... La lista devuelta con el respaldo de esta lista.

Lo que se está almacenando en messagesInFlightuna vista , no una copia . Cuando elimina los primeros mensajes de messages, usted es, de hecho, la eliminación de los mismos mensajes que tenía en su messagesInFlightjusto después de subList()la llamada. Así que después del forbucle, habrá mensajes completamente diferentes, y los primeros n mensajes se pierde por completo.


En cuanto a por qué usted está recibiendo el error que se ve - subList()permite específicamente cambios no estructurales a ambas sub-lista y la lista original (medios no estructurales sustitución de los elementos, no añadiendo o quitando ellos), y el ejemplo en la documentación también presenta la forma en la lista original puede ser modificado mediante la modificación de la sub-lista. Sin embargo, no está permitido modificar la lista original y luego acceder a ella a través de la sub-lista y puede dar lugar a ConcurrentModifcationException, de manera similar a lo que sucede cuando se cambia una lista que está iteración a través de un repetidor.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=225706&siteId=1
Recomendado
Clasificación