2008-10-16 4 views
7

Tengo un objeto de lista que se accede por varios subprocesos. Hay principalmente un hilo, y en algunas condiciones dos hilos, que actualiza la lista. Hay de uno a cinco hilos que pueden leerse de esta lista, dependiendo de la cantidad de solicitudes de usuario que se procesen. La lista no es una cola de tareas para realizar, es una lista de objetos de dominio que se están recuperando y actualizando simultáneamente.mejor enfoque para utilizar en Java 6 para una lista que se accede simultáneamente

Ahora bien, hay varias maneras de hacer que el acceso a esta lista de temas de seguridad:
-uso bloque sincronizado
-uso normales bloqueo (es decir, operaciones de lectura y escritura comparten misma cerradura)
-uso ReadWriteLock
-uso una de las nuevas ConcurrentBLABLBA clases de colección

Mi pregunta:
¿Cuál es el enfoque óptimo para utilizar, dado que las secciones pericárdico subsidiario de pericardiocentesis no suelen contener una gran cantidad de operaciones (en su mayoría sólo añadir/eliminar/insertar o conseguir los elementos de la lista)?
¿Puede recomendar otro enfoque, no mencionado anteriormente?

Algunas limitaciones
rendimiento -optimal es crítica, el uso de memoria no tanto
-debe ser una lista ordenada (en la actualidad la sincronización en un ArrayList ), aunque no es una lista ordenada (es decir, no ordenados usando Comparable o Comparator, pero de acuerdo con el orden de inserción)
-la lista será grande, contiene hasta 100000 objetos de dominio, por lo que no es posible usar algo como CopyOnWriteArrayList
-las secciones de escritura/actualización suelen ser muy rápidas, haciendo simples add/eliminar/insertar o reemplazar (establecer)
-la operaciones de lectura harán todo una elementAt (índice) llame a la mayor parte del tiempo, aunque algunas operaciones de lectura podrían hacer una búsqueda binaria o indexOf (elemento)
-no iteración directa sobre la lista se hace, a pesar de operación como indexOf (..) va a recorrer la lista

Respuesta

3

¿Tiene que usar una lista secuencial? Si una estructura tipo mapa es más apropiada, puede usar un ConcurrentHashMap. Con una lista, un ReadWriteLock es probablemente la forma más efectiva.

Edición para reflejar edición de OP: La búsqueda binaria en el orden de inserción? ¿Almacena una marca de tiempo y la usa para comparar en su búsqueda binaria? Si es así, es posible que pueda usar la marca de tiempo como la clave y ConcurrentSkipListMap como el contenedor (que mantiene el orden de las llaves).

+0

Me gusta la idea de ConcurrentSkipListMap. En el 90% de las veces, la lista se ordena de acuerdo con una marca de tiempo (parte de la ID de cada objeto de dominio), por lo que probablemente valga la pena optimizarla para eso. Todavía pensarás en el otro 10%. –

1

¿Qué están haciendo los hilos de lectura? Si están iterando sobre la lista, entonces realmente debe asegurarse de que nadie toque la lista durante todo el proceso de iteración, de lo contrario, podría obtener resultados muy extraños.

Si puede definir con precisión qué semántica necesita, debería ser posible resolver el problema, pero es posible que necesite escribir su propio tipo de colección para hacerlo de forma adecuada y eficiente. Alternativamente, CopyOnWriteArrayList bien puede ser lo suficientemente bueno, si es potencialmente costoso. Básicamente, cuanto más puedas atar tus requisitos, más eficiente puede ser.

+0

He echado un vistazo a CopyOnWriteArrayList, pero sería demasiado caro. La lista puede tener 100000 elementos y se actualiza mucho. –

+2

De acuerdo, en cuyo caso necesitarás trabajar tu semántica cuidadosamente. ¿Estas actualizaciones realizan inserciones/eliminaciones, o simplemente reemplazan elementos? Si están agregando/eliminando, ¿lo están haciendo a la cabeza/cola de la lista? (En cuyo caso, una lista vinculada realmente podría ayudarlo.) –

1

No sé si esto es una posible solución para el problema, pero ... que tiene sentido para mí utilizar un gestor de base de datos para mantener ese enorme cantidad de datos y se deja manejar las transacciones

+0

Los datos se gestionan en el lado del servidor por el gestor de base de datos y la capa Appserver, pero tenemos que mostrarlo de alguna manera al usuario final.El managmenet de esta lista ocurre en el lado del cliente donde se recuperan los datos. –

1

I segundo Telcontar's suggestion de una base de datos, ya que en realidad están diseñados para administrar esta escala de datos y negociar entre subprocesos, mientras que las colecciones en memoria no lo son.

Usted dice que los datos están en una base de datos en el servidor, y la lista local en los clientes es por el bien de la interfaz del usuario. No debería tener que mantener todos los 100000 elementos en el cliente a la vez, ni realizar ediciones tan complicadas en él. Me parece que lo que quiere en el cliente es un caché ligero en la base de datos.

Escriba una memoria caché que almacena solo el subconjunto actual de datos en el cliente a la vez. Este caché de cliente no realiza ediciones multiproceso complejas en sus propios datos; en su lugar, envía todas las ediciones al servidor y escucha las actualizaciones. Cuando los datos cambian en el servidor, el cliente simplemente olvida datos antiguos y los carga de nuevo. Solo un hilo designado puede leer o escribir la colección en sí. De esta forma, el cliente simplemente refleja las ediciones que ocurren en el servidor, en lugar de necesitar ediciones complicadas por sí mismo.

Sí, esta es una solución bastante complicada. Los componentes de la misma son:

  • Un protocolo para cargar un rango de los datos, dicen artículos 478712-478901, en lugar de todo el asunto
  • Un protocolo para recibir actualizaciones acerca de los datos modificados
  • Una clase caché que almacena elementos por su índice conocido en el servidor
  • Un hilo que pertenece a ese caché que se comunicó con el servidor. Este es el único hilo que escribe en la propia colección
  • Un hilo perteneciente a ese caché que procesa las devoluciones de llamada cuando los datos se recuperan
  • Una interfaz que los componentes de interfaz de usuario implementan para permitir que ellos para recibir los datos cuando se ha cargado

al primer intento, los huesos de este caché podría ser algo como esto:

class ServerCacheViewThingy { 
    private static final int ACCEPTABLE_SIZE = 500; 
    private int viewStart, viewLength; 
    final Map<Integer, Record> items 
      = new HashMap<Integer, Record>(1000); 
    final ConcurrentLinkedQueue<Callback> callbackQueue 
      = new ConcurrentLinkedQueue<Callback>(); 

    public void getRecords (int start, int length, ViewReciever reciever) { 
     // remember the current view, to prevent records within 
     // this view from being accidentally pruned. 
     viewStart = start; 
     viewLenght = length; 

     // if the selected area is not already loaded, send a request 
     // to load that area 
     if (!rangeLoaded(start, length)) 
      addLoadRequest(start, length); 

     // add the reciever to the queue, so it will be processed 
     // when the data has arrived 
     if (reciever != null) 
      callbackQueue.add(new Callback(start, length, reciever)); 
    } 

    class Callback { 
     int start; 
     int length; 
     ViewReciever reciever; 
     ... 
    } 

    class EditorThread extends Thread { 

     private void prune() { 
      if (items.size() <= ACCEPTABLE_SIZE) 
       return; 
      for (Map.Entry<Integer, Record> entry : items.entrySet()) { 
       int position = entry.key(); 
       // if the position is outside the current view, 
       // remove that item from the cache 
       ... 
      } 
     } 

     private void markDirty (int from) { ... } 

     .... 
    } 

    class CallbackThread extends Thread { 
     public void notifyCallback (Callback callback); 
     private void processCallback (Callback) { 
      readRecords 
     } 
    } 
} 

interface ViewReciever { 
    void recieveData (int viewStart, Record[] records); 
    void recieveTimeout(); 
} 

Hay una gran cantidad de detalles que tendrá que llenar por sí mismo, obviamente.

+0

Gracias por su respuesta. Los hasta 100000 elementos son la "página" de datos que estamos recuperando para el cliente. El DB puede contener miles de millones de entradas. Lo que consideraría seriamente es su consejo para acceder a la lista con solo un hilo. Esto definitivamente simplificaría las cosas. –

1

Se puede utilizar un envoltorio que implementa la sincronización:

import java.util.Collections; 
import java.util.ArrayList; 

ArrayList list = new ArrayList(); 
List syncList = Collections.synchronizedList(list); 

// make sure you only use syncList for your future calls... 

Esta es una solución fácil. Intentaré esto antes de recurrir a soluciones más complicadas.

Cuestiones relacionadas