2016-09-02 20 views
6

Tôi nhận thấy rằng java.iojava.nio việc triển khai tệp truy cập ngẫu nhiên hơi khác so với cách xử lý FileLocks.Tệp truy cập ngẫu nhiên FileLock: java.io vs. java.nio

Dường như (trên Windows) java.io cung cấp cho bạn khóa tệp bắt buộc và java.nio cung cấp cho bạn khóa tệp tư vấn khi yêu cầu tương ứng. Khóa tệp bắt buộc có nghĩa là khóa giữ cho tất cả các quy trình và lưu giữ tư vấn cho các quy trình hoạt động tốt tuân theo cùng một giao thức khóa.

Nếu tôi chạy ví dụ sau, tôi có thể xóa tệp *.nio theo cách thủ công, trong khi *.io tệp sẽ không bị xóa.

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(); 
    } 

} 

Có lý do nào cho điều này không? Hai cái này có được coi là lựa chọn thay thế hay chúng có nghĩa là cùng tồn tại vô thời hạn?

+0

Việc thêm 'SYNC, DSYNC' vào phiên bản nio có tạo sự khác biệt không? Sau đó, nó sẽ có được một xem xét hiệu suất. –

+0

thêm 'SYNC, DSYNC' không tạo sự khác biệt – starikoff

Trả lời

6

TL; DR: Nó không phải về khóa, đó là cách mở tệp. Những gì bạn thấy trong io là Windows tốt nhất có thể làm trước Windows 2000, và nó đã làm điều đó ngay cả khi các tệp được mở để chỉ đọc - nó không thể xóa tệp đó. Những gì bạn thấy trong nio là cải tiến sử dụng khả năng mới được giới thiệu từ Windows 2000, nhưng bạn vẫn có thể có hành vi cũ của mình trong nio nếu bạn chọn. Nó đã được quyết định không để cổng khả năng đó vào những gì io nào.

Câu chuyện đầy đủ: Tôi đã xóa tất cả mã liên quan đến khóa (xem bên dưới) cũng như ghi vào tệp và nó hoạt động hoàn toàn giống với mã của bạn; tuy nhiên, tôi cũng thấy rằng nếu bạn chỉ định ExtendedOpenOption.NOSHARE_DELETE khi mở kênh "nio", thì hành vi khi bạn cố xóa bất kỳ tệp nào trong số hai tệp này giống nhau (bỏ ghi chú trong mã và thử). Cũng tìm thấy this question và nó có một số lời giải thích, không chắc chắn nếu nó sẽ giúp đỡ.

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); 
    } 
} 

Cập nhật 1: Thực ra, tôi vẫn không biết lý do đằng sau này, nhưng cơ chế rõ ràng: RandomAccessFile constructor cuối cùng gọi là mã gốc sau đây từ method winFileHandleOpen từ 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); 
    ... 
} 

Vì vậy, FILE_SHARE_DELETE cờ không được đặt và bạn không thể làm gì để đặt nó. Mặt khác, gọi java.nio.channels.FileChannel.open(Path, OpenOption...)eventually mất cờ này vào tài khoản:

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); 
     ... 
    } 

Cập nhật 2: Từ JDK-6607535:

Các gợi ý để thay đổi chế độ chia sẻ là RFE 6357433. Nó sẽ là tuyệt vời để làm điều đó nhưng nó có thể phá vỡ các ứng dụng hiện có (vì hành vi hiện tại đã tồn tại kể từ jdk1.0). Thêm một chế độ đặc biệt để RandomAccessFile để làm việc xung quanh một vấn đề Windows có lẽ không phải là cách tiếp cận đúng. Ngoài ra, có một số gợi ý rằng điều này có thể giúp giải quyết vấn đề xóa các tệp có ánh xạ tệp. Đây không phải là như vậy, như là một ánh xạ tập tin vẫn ngăn cản một tập tin bị xóa. Dù sao, đối với jdk7 chúng tôi đang làm việc trên một API hệ thống tệp mới sẽ giải quyết một số yêu cầu ở đây. Việc triển khai Windows thực hiện điều đúng để các tệp đã mở có thể bị xóa.

Xem thêm JDK-6357433 tham chiếu từ đó:

Một khách hàng đã chỉ ra trên các diễn đàn java.net rằng việc mở một tập tin trên Windows sử dụng một FileInputStream gây quá trình khác là không thể xóa các tập tin. Đây là hành vi dự định khi mã hiện đang được viết, vì các cờ chia sẻ được chỉ định trong khi mở tệp là FILE_SHARE_READ và FILE_SHARE_WRITE. Xem io_util_md.c, fileOpen. Tuy nhiên, Windows 2000 cung cấp một lá cờ mới, FILE_SHARE_DELETE, cho phép một quá trình khác để xóa tệp trong khi nó vẫn mở.

và cuối cùng

làm việc xung quanh

Đối JDK7, thực hiện giải pháp là sử dụng hệ thống tập tin mới API. Vì vậy, thay vì FileInputStream mới (f) và FileOutputStream mới (f), sử dụng f.toPath(). NewInputStream() và f.toPath(). NewOutputStream().

+1

Phân tích tuyệt vời. Tôi thậm chí không bao giờ coi ổ khóa không gây ra điều này. – predi

Các vấn đề liên quan