2012-02-15 10 views
11

Quiero ser capaz de bloquear basado en una jerarquía del sistema de archivos. Por ejemplo:Bloqueos mutex jerárquicos en Java

Tema 1:

lock("/"); 
doStuff(); 
unlock(); 

Tema 2:

lock("/sub/foo"); 
doStuff(); 
unlock(); 

Tema 3:

lock("/sub/bar"); 
doStuff(); 
unlock(); 

Si el hilo 1 adquiere el bloqueo primero y luego los hilos 2 y 3 se estar bloqueado hasta que se abra el Tema 1. Sin embargo, si el Tema 2 primero adquiere el bloqueo, entonces el Tema 3 debería poder ejecutarse al mismo tiempo que el Tema 2. La regla general es que si hay un bloqueo en un directorio padre, entonces el hilo debe bloquearse.

¿Tiene Java algo incorporado que pueda ayudar a resolver esto? Quiero evitar almacenar un bloqueo por directorio porque habrá cientos de miles de directorios.

+0

+1 por una pregunta muy interesante. ¿Se ha especificado la estructura del directorio por adelantado o se pueden crear "archivos" ad-hoc? Además, ¿cuál es el sistema de prioridad si un subproceso quiere obtener el directorio raíz mientras que muchos otros subprocesos intentan obtener archivos individuales?¿El hilo que quiere la raíz simplemente se muere de hambre, o hay algún tipo de garantía que tenga en mente? – templatetypedef

+0

Supongo que si el hilo 2 adquiere el bloqueo, el hilo 1 debería bloquearse, ¿no? – Irfy

+0

Parte de la motivación detrás de esto es modificar el sistema de archivos, por lo que las rutas cambiarían constantemente. No estoy seguro de cómo manejar la falta de hilo ... eso es algo que realmente no he considerado. Supongo que un mecanismo de bloqueo de lectura/escritura podría acercarse a resolver esto y ser capaz de manejar hilos no muertos de hambre. –

Respuesta

7

Me almacenar las rutas de directorio en un árbol como este:

-/
- sub 
    - foo 
    - bar 

Siempre que necesite para bloquear cualquier cosa en ese árbol hay que bajar de la raíz y adquirir una lectura de bloqueo en todo excepto en el blanco nodo en sí. El nodo de destino obtiene un bloqueo de escritura.

Este esquema le garantiza la libertad de bloqueo y la estabilidad de las partes relevantes del árbol.

No veo un problema particular que almacene cientos de miles de bloqueos. Esto probablemente desperdiciará quizás 100 bytes de RAM por bloqueo. Pero simplifica la arquitectura. ¿Mide si realmente es un problema?

Como alternativa, puede tener un mapa de la ruta de acceso al bloqueo. Todas las operaciones en ese diccionario deben ser sincronizadas por las personas que llaman. Esto le permite inicializar lentamente bloqueos. También puede recolectar basura periódicamente bloqueos sin usar primero tomando un bloqueo de escritura en la raíz que quita todas las operaciones. Una vez que todo está en silencio, descarta todos los bloqueos que no sean root.

+1

enfoque fresco. Creo que eso podría funcionar Si tengo que almacenar un bloqueo por directorio, que así sea. Solo estaba tratando de evitarlo si es posible. –

+0

Así es como las bases de datos cierran. Bloquean la ruta del árbol B desde la raíz hasta las páginas de la hoja. – usr

+0

Esto se ve bien. Iba con 'java.util.ConcurrentHashMap' para obtener el mejor rendimiento y lo mapeaba como' '. También usaría 'File file = new File (stringPath) .getCanonicalFile()' para garantizar la exclusividad. La lógica debería ser bastante clara dada la creación de bloqueo diferido y el bloqueo por separado para lectura y escritura. – Irfy

0

Puede haber una solución más efectiva, pero así es como comenzaría.

Crearía un objeto TreeAccess compartido, con un método lock(path) y unlock(path). Este método tendría que ingresar a un bloque sincronizado que se repetirá hasta que la ruta esté disponible. En cada iteración, si no está disponible, se verificará si la ruta está disponible, y si no, wait() hasta que otro hilo llame al notifyAll(). Si la ruta está disponible, procedería y, cuando termine, llame al método de desbloqueo() que sería notifyAll().

Antes de continuar, debe almacenar la ruta bloqueada en alguna estructura de datos. Y antes de notificar, debe eliminar la ruta desbloqueada de esta estructura de datos. Para verificar si hay una ruta disponible, debe encontrar si existe alguna ruta en esta estructura de datos que sea igual o que sea un antecesor de la ruta que desea bloquear.

public void lock(String path) { 
    synchronized (lock) { 
     while (!available(path)) { 
      lock.wait(); 
     } 
     lockedPaths.add(path); 
    } 
} 

public void unlock(String path) { 
    synchronized (lock) { 
     lockedPaths.remove(path); 
     lock.notifAll(); 
    } 
} 

private boolean available(String path) { 
    for (String lockedPath : lockedPaths) { 
     if (isParentOrEqual(lockedPath, path) { // this method is left as an exercise 
      return false; 
     } 
    } 
    return true; 
}