java file-io locking

java - ¿Cómo funciona FileLock?



file-io locking (5)

Debes liberar el archivo con la liberación del método () antes de realizar cualquier acción como cambiar el nombre, eliminar o ...

He intentado usar FileLock para obtener acceso exclusivo a un archivo para:

  • bórralo
  • cambiarle el nombre
  • escribe a ella

Porque en Windows (al menos) parece que no puede eliminar, renombrar ni escribir en un archivo que ya está en uso. El código que he escrito se ve así:

import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; public abstract class LockedFileOperation { public void execute(File file) throws IOException { if (!file.exists()) { throw new FileNotFoundException(file.getAbsolutePath()); } FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); try { // Get an exclusive lock on the whole file FileLock lock = channel.lock(); try { doWithLockedFile(file); } finally { lock.release(); } } finally { channel.close(); } } public abstract void doWithLockedFile(File file) throws IOException; }

Aquí hay algunas pruebas unitarias que demuestran el problema. Deberá tener Apache commons-io en su classpath para ejecutar la tercera prueba.

import java.io.File; import java.io.IOException; import junit.framework.TestCase; public class LockedFileOperationTest extends TestCase { private File testFile; @Override protected void setUp() throws Exception { String tmpDir = System.getProperty("java.io.tmpdir"); testFile = new File(tmpDir, "test.tmp"); if (!testFile.exists() && !testFile.createNewFile()) { throw new IOException("Failed to create test file: " + testFile); } } public void testRename() throws IOException { new LockedFileOperation() { @Override public void doWithLockedFile(File file) throws IOException { if (!file.renameTo(new File("C:/Temp/foo"))) { fail(); } } }.execute(testFile); } public void testDelete() throws IOException { new LockedFileOperation() { @Override public void doWithLockedFile(File file) throws IOException { if (!file.delete()) { fail(); } } }.execute(testFile); } public void testWrite() throws IOException { new LockedFileOperation() { @Override public void doWithLockedFile(File file) throws IOException { org.apache.commons.io.FileUtils.writeStringToFile(file, "file content"); } }.execute(testFile); } }

Ninguna de las pruebas pasa. Los primeros 2 fallan, y el último arroja esta excepción:

java.io.IOException: The process cannot access the file because another process has locked a portion of the file at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:247) at org.apache.commons.io.IOUtils.write(IOUtils.java:784) at org.apache.commons.io.IOUtils.write(IOUtils.java:808) at org.apache.commons.io.FileUtils.writeStringToFile(FileUtils.java:1251) at org.apache.commons.io.FileUtils.writeStringToFile(FileUtils.java:1265)

Parece que el método lock() coloca un bloqueo en el archivo que me impide cambiar el nombre / eliminarlo / escribirlo. Mi suposición era que bloquear el archivo me daría acceso exclusivo al archivo, por lo que podría cambiarle el nombre / eliminarlo / escribirlo sin preocuparme si otro proceso también lo está accediendo.

O entiendo mal FileLock o no es una solución adecuada para mi problema.



El mensaje sobre otro proceso solo significa que algún proceso en su sistema tiene el archivo abierto. En realidad, no comprueba que ese proceso sea el mismo que el que intenta eliminar / cambiar el nombre del archivo. En este caso, el mismo programa tiene el archivo abierto. Lo has abierto para obtener el candado. El bloqueo aquí tiene poco o ningún valor, especialmente si está haciendo esto para borrar o cambiar el nombre de las operaciones.

Para hacer lo que desee, deberá bloquear la entrada del directorio. Esto no está disponible en Java y puede no estar disponible en Windows. Estas operaciones (eliminar e insertar) son atómicas. Eso significa que el sistema operativo se encarga de bloquear el directorio y otras estructuras del sistema de archivos por usted. Si otro proceso (o el suyo) tiene el archivo abierto, entonces estas operaciones fallarán. Si está intentando bloquear el archivo exclusivamente (entrada de directorio) y otro proceso (o el suyo propio) tiene el archivo abierto, entonces el bloqueo fallará. No hay diferencia, pero intentar hacer el bloqueo simplemente complica y, en este caso, hace que la operación sea imposible (es decir, los archivos siempre se abren antes de intentar realizar la operación).

Ahora escribir en el archivo es una operación de bloqueo válida. Bloquee el archivo o parte del archivo en el que desea escribir y luego funcionará. En Windows, este mecanismo de bloqueo es obligatorio, por lo que otro descriptor abierto / archivo no podrá escribir en ninguna parte que esté bajo el bloqueo.

EDITAR

Según FileChannel.lock en FileChannel.lock , es lo mismo que llamar a FileChannel.lock(0L, Long.MAXVALUE, false) . Este es un bloqueo exclusivo en una región desde el primer byte hasta el último.

En segundo lugar, según JavaDoc en FileLock

Si un bloqueo realmente impide que otro programa acceda al contenido de la región bloqueada depende del sistema y, por lo tanto, no está especificado. Las instalaciones nativas de bloqueo de archivos de algunos sistemas son meramente de asesoramiento, lo que significa que los programas deben observar cooperativamente un protocolo de bloqueo conocido para garantizar la integridad de los datos. En otros sistemas, los bloqueos de archivos nativos son obligatorios, lo que significa que si un programa bloquea una región de un archivo, se impide que otros programas accedan a esa región de una manera que viole el bloqueo. En otros sistemas, si los bloqueos de archivos nativos son de carácter consultivo u obligatorio se pueden configurar por archivo. Para garantizar un comportamiento coherente y correcto en todas las plataformas, se recomienda encarecidamente que los bloqueos proporcionados por esta API se utilicen como si fueran bloqueos de aviso .

EDITAR

Para el método testWrite . JavaDoc en el método estático de E / S común es escaso pero dice "Escribe una cadena en un archivo creando el archivo si no existe ..." y como este método toma un File lugar de una secuencia abierta, es probable que abra el File archivo internamente Probablemente no abra el archivo con acceso compartido y también se abra para agregar acceso. Esto significa que la apertura y el bloqueo existentes (su apertura para obtener el canal del que se obtiene el bloqueo) están bloqueando ese uso. Para comprender aún más, necesitaría obtener la fuente de ese método y ver lo que está haciendo.

EDITAR

Lo siento, estoy corregido. Revisé la API de Windows y el bloqueo de archivos es obligatorio en Windows. Esta es la razón por la cual la escritura falla. El primer abierto (su new RandomAccessFile ) y el bloqueo tienen el archivo bloqueado. La apertura para escribir la cadena tiene éxito pero la escritura falla porque otro abierto (descriptor de archivo) tiene la extensión total del archivo bajo el bloqueo exclusivo obligatorio, es decir, ningún otro descriptor de archivo puede escribir en el archivo hasta que se libere el bloqueo.

Tenga en cuenta que el bloqueo está asociado con el descriptor de archivo NOT process o thread.


Las operaciones de delete y rename las realiza el sistema operativo y son atómicas (en la mayoría de los sistemas operativos), por lo que no se requiere bloqueo.

Para escribir una cadena en un archivo, sería más simple escribir en un archivo temporal primero (por ejemplo, foo.tmp) y luego cambiarle el nombre una vez que esté listo.


Los bloqueos de archivos Java se especifican solo para proteger contra otros bloqueos y nada más. Cómo se comportan en plataformas específicas, es decir, cualquier semántica adicional, es específico de la plataforma.