2016-09-02 14 views
6

Я заметил, что java.io и java.nio реализация файлов произвольного доступа несколько отличается от того, как обрабатываются FileLocks.Файл произвольного доступа FileLock: java.io vs. java.nio

Кажется, что (в Windows) java.io предоставляет обязательную блокировку файла, а java.nio предоставляет вам блокировку консультативного файла при запросе его соответственно. Обязательная блокировка файлов означает, что блокировка выполняется для всех процессов и содержит рекомендации для процессов велляции, которые следуют одному протоколу блокировки.

Если я запустил следующий пример, я могу удалить файл *.nio вручную, а файл *.io отказывается быть удаленным.

import java.io.*; 
import java.lang.management.ManagementFactory; 
import java.nio.*; 
import java.nio.channels.*; 
import java.nio.file.*; 

public class NioIoLock { 

    public static void main(String[] args) throws IOException, InterruptedException { 
     String workDir = System.getProperty("user.dir"); 

     FileChannel channelIo, channelNio; 
     FileLock lockIo, lockNio; 

     // use io 
     { 
      String fileName = workDir 
        + File.separator 
        + ManagementFactory.getRuntimeMXBean().getName() 
        + ".io"; 
      File lockFile = new File(fileName); 
      lockFile.deleteOnExit(); 
      RandomAccessFile file = new RandomAccessFile(lockFile, "rw");    

      channelIo = file.getChannel(); 
      lockIo = channelIo.tryLock(); 
      if (lockIo != null) {     
       channelIo.write(ByteBuffer.wrap("foobar".getBytes("UTF-8"))); 
      } 

     } 

     // use nio 
     { 
      Path workDirPath = Paths.get(workDir); 
      Path file = workDirPath.resolve(
        Paths.get(ManagementFactory.getRuntimeMXBean().getName() + ".nio")); 

      // open/create test file 
      channelNio = FileChannel.open(
        file, StandardOpenOption.READ, StandardOpenOption.WRITE, 
        StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE); 

      // lock file 
      lockNio = channelNio.tryLock(); 
      if (lockNio != null) { 
       channelNio.write(ByteBuffer.wrap("foobar".getBytes("UTF-8"))); 
      } 

     } 

     // do not release locks for some time 
     Thread.sleep(10000); 

     // release io lock and channel 
     if (lockIo != null && lockIo.isValid()) { 
      lockIo.release(); 
     } 
     channelIo.close(); 

     // release nio lock and channel 
     if (lockNio != null && lockNio.isValid()) { 
      lockNio.release(); 
     } 
     channelNio.close(); 
    } 

} 

Есть ли причина для этого? Являются ли эти два даже альтернативными вариантами или они предназначены для сосуществования бесконечно?

+0

Будет ли добавление 'SYNC, DSYNC' в версию nio изменить ситуацию? Тогда это было бы рассмотрением производительности. –

+0

добавление 'SYNC, DSYNC' не имеет значения – starikoff

ответ

6

TL; Речь идет не о замках, а о том, как файлы открываются. То, что вы видите в io, является лучшим Windows, которое можно было сделать до Windows 2000, и это было сделано даже тогда, когда файлы были открыты только для чтения - удалить этот файл не удалось. То, что вы видите в nio, является улучшением, которое использует новые возможности, введенные с Windows 2000, но вы по-прежнему можете иметь свое старое поведение в nio, если захотите. Было решено не переносить эту возможность на то, что делает io.

Полная версия: Я удалил весь код, связанный с блокировкой (см. Ниже), а также записывая файлы, и он работает абсолютно так же, как ваш код; однако я также обнаружил, что если вы укажете при открытии канала «nio», тогда поведение при попытке удалить любой из двух файлов будет одинаковым (раскомментируйте его в коде и попробуйте). Также найдено this question, и у него есть некоторые объяснения, не уверен, что это поможет.

import java.io.*; 
import java.lang.management.ManagementFactory; 
import java.nio.*; 
import java.nio.channels.*; 
import java.nio.file.*; 

public class NioIoLock { 

    public static void main(String[] args) 
      throws IOException, InterruptedException { 
     String workDir = System.getProperty("user.dir"); 

     FileChannel channelIo, channelNio; 
     FileLock lockIo, lockNio; 

     // use io 
     { 
      String fileName = workDir + File.separator 
       + ManagementFactory.getRuntimeMXBean().getName() + ".io"; 
      File lockFile = new File(fileName); 
      lockFile.deleteOnExit(); 
      RandomAccessFile file = new RandomAccessFile(lockFile, "rw"); 
      channelIo = file.getChannel(); 
     } 

     // use nio 
     { 
      Path workDirPath = Paths.get(workDir); 
      Path file = workDirPath.resolve(Paths 
       .get(ManagementFactory.getRuntimeMXBean().getName() + ".nio")); 

      // open/create test file 
      channelNio = FileChannel.open(file, StandardOpenOption.READ, 
       StandardOpenOption.WRITE, StandardOpenOption.CREATE, 
       StandardOpenOption.DELETE_ON_CLOSE 
       /*, com.sun.nio.file.ExtendedOpenOption.NOSHARE_DELETE*/); 
     } 

     // do not release locks for some time 
     Thread.sleep(10000); 
    } 
} 

Update 1: На самом деле, я до сих пор не знаю, чем это объясняется, но механики очевидны: RandomAccessFile конструктор в конечном счете, вызывает следующий машинный код из method winFileHandleOpen из io_util_md.c:

FD 
winFileHandleOpen(JNIEnv *env, jstring path, int flags) 
{ 
    ... 
    const DWORD sharing = 
     FILE_SHARE_READ | FILE_SHARE_WRITE; 
    ... // "sharing" not updated anymore 
    h = CreateFileW(
     pathbuf,   /* Wide char path name */ 
     access,    /* Read and/or write permission */ 
     sharing,   /* File sharing flags */ 
     NULL,    /* Security attributes */ 
     disposition,  /* creation disposition */ 
     flagsAndAttributes, /* flags and attributes */ 
     NULL); 
    ... 
} 

Итак, флаг FILE_SHARE_DELETE не установлен, и вы ничего не можете сделать, чтобы его установить. С другой стороны, называя java.nio.channels.FileChannel.open(Path, OpenOption...)eventually принимает этот флаг во внимание:

private static FileDescriptor open(String pathForWindows, 
             String pathToCheck, 
             Flags flags, 
             long pSecurityDescriptor) 
     throws WindowsException 
    { 
     ... 
     int dwShareMode = 0; 
     if (flags.shareRead) 
      dwShareMode |= FILE_SHARE_READ; 
     if (flags.shareWrite) 
      dwShareMode |= FILE_SHARE_WRITE; 
     if (flags.shareDelete) 
      dwShareMode |= FILE_SHARE_DELETE; 
     ... 
     // open file 
     long handle = CreateFile(pathForWindows, 
           dwDesiredAccess, 
           dwShareMode, 
           pSecurityDescriptor, 
           dwCreationDisposition, 
           dwFlagsAndAttributes); 
     ... 
    } 

Update 2: От JDK-6607535:

предложением изменить режим обмена является RFE 6357433. Было бы здорово для этого, но он может нарушить существующие приложения (поскольку текущее поведение существует с jdk1.0). Добавление специального режима в RandomAccessFile для работы с проблемами Windows, вероятно, тоже не подходит. Кроме того, есть некоторые предположения, что это может помочь с проблемой удаления файлов, имеющих сопоставление файлов. Это не так, поскольку сопоставление файлов по-прежнему предотвращает удаление файла. В любом случае, для jdk7 мы работаем над новым API файловой системы, который рассмотрит некоторые из требований здесь. Реализация Windows делает то, что открытые файлы можно удалить.

Также см JDK-6357433 ссылки оттуда:

Клиент указал на java.net форумах, что открытие файла на Windows, используя FileInputStream вызывает другие процессы, чтобы быть не в состоянии удалить файл. Это предполагаемое поведение, поскольку код в настоящее время написан, так как флаги обмена, указанные во время открытия файла, - FILE_SHARE_READ и FILE_SHARE_WRITE. См. Io_util_md.c, fileOpen. Однако Windows 2000 предоставляет новый флаг FILE_SHARE_DELETE, который позволяет другому процессу удалять файл, пока он еще открыт.

и, наконец,

РАБОТА ВОКРУГ

Для jdk7, обходной путь заключается в использовании новой файловой системы API. Поэтому вместо нового FileInputStream (f) и нового FileOutputStream (f) используйте f.toPath(). NewInputStream() и f.toPath(). NewOutputStream().

+1

Отличный анализ. Я даже не рассматривал блокировки, не вызывающие этого. – predi