2009-02-27 24 views
30

Tôi có một cụm máy, mỗi máy chạy một ứng dụng Java.Làm thế nào để đổi tên nguyên tử một tệp trong Java, ngay cả khi tệp đích đã tồn tại?

Các ứng dụng Java này cần truy cập một tệp resource.txt duy nhất một cách đồng thời.

Tôi cần phải đổi tên nguyên tắc tệp temp.txt thành resource.txt bằng Java, ngay cả khi resource.txt đã tồn tại.

Xóa resource.txt và đổi tên temp.txt không hoạt động, vì nó không phải nguyên tử (nó tạo ra một khung thời gian nhỏ, nơi resource.txt không tồn tại).

Và nó phải là nền tảng chéo ...

Cảm ơn!

+0

Note tự, 8 năm sau: Tôi không nhớ bối cảnh, nhưng nếu * Vấn đề * đòi hỏi cross-platform số nguyên tử, có thể * giải pháp * không nên liên quan đến một tập tin? –

Trả lời

12

Trên Linux (và tôi tin rằng Solaris và các hệ điều hành UNIX khác), phương thức File.renameTo() của Java sẽ ghi đè tệp đích nếu nó tồn tại, nhưng đây không phải là trường hợp trong Windows.

Để có nền tảng chéo, tôi nghĩ bạn phải sử dụng khóa tệp trên resource.txt và sau đó ghi đè lên dữ liệu.

Hành vi của khóa tệp là phụ thuộc vào nền tảng. Trên một số nền tảng, khóa tệp là lời khuyên, có nghĩa là trừ khi ứng dụng kiểm tra khóa tệp, nó sẽ không bị ngăn truy cập tệp. Trên các nền tảng khác, khóa tệp là bắt buộc, có nghĩa là khóa tệp ngăn chặn bất kỳ ứng dụng nào truy cập vào tệp .

Linux, theo mặc định, sử dụng khóa tự nguyện trong khi Windows thực thi khóa đó. Có lẽ bạn có thể phát hiện hệ điều hành và sử dụng renameTo() trong UNIX với một số mã khóa cho Windows?

Ngoài ra còn có một cách để bật khóa bắt buộc trong Linux cho các tệp cụ thể, nhưng nó không rõ ràng. Bạn phải thiết lập các bit chế độ vừa phải.

Linux, sau System V (xem hệ thống V Interface Definition (SVID) Phiên bản 3), cho phép các bit sgid cho các tập tin mà không nhóm thực hiện phép đánh dấu file cho bắt buộc khóa

+0

điều này sẽ được đánh dấu là không được chấp nhận, vì cách mới nên là java.nio! – user1052080

+0

Điều này chỉ hoạt động nếu tệp quá nhỏ để có thể ghi đè trong một thao tác ghi. –

+0

@ user1052080 Thật không may NIO chỉ đi một phần để giải quyết vấn đề này. NIO không hỗ trợ đồng bộ hóa các thao tác tệp khác với ghi từ luồng. Điều này đôi khi là một vấn đề quan trọng. – Leliel

1

Nếu điều này nên là nền tảng chéo, tôi đề xuất 2 tùy chọn:

  1. Triển khai dịch vụ trung gian chịu trách nhiệm cho tất cả các truy cập tệp. Ở đây bạn có thể sử dụng một số cơ chế để đồng bộ hóa các yêu cầu. Mỗi ứng dụng java của khách hàng chỉ truy cập tệp thông qua dịch vụ này.
  2. Tạo một điều khiển mỗi khi bạn cần thực hiện các thao tác đã đồng bộ hóa.Mỗi ứng dụng java truy cập tệp đều chịu trách nhiệm kiểm tra tệp kiểm soát và chờ trong khi tệp này tồn tại kiểm soát. (gần giống như một semaphore). Quá trình thực hiện thao tác xóa/đổi tên có trách nhiệm tạo/xóa tệp kiểm soát.
+0

Điều gì về việc người dùng xóa tệp bằng tay bên ngoài Java? Bạn không thể kiểm soát rằng tất cả truy cập tệp đi qua dịch vụ Java. – TofuBeer

+0

Hãy nhớ rằng bằng cách sử dụng một tập tin điều khiển là không đủ để thực hiện khóa tập tin an toàn! – user3151902

2

Như đã nêu here, có vẻ như hệ điều hành Windows thậm chí không hỗ trợ đổi tên tệp nguyên tử cho các phiên bản cũ hơn. Rất có thể bạn phải sử dụng một số cơ chế khóa thủ công hoặc một số loại giao dịch. Để làm được điều đó, bạn có thể muốn xem xét gói apache commons transaction.

1

Nếu mục đích của việc đổi tên là để thay thế resource.txt on the fly bạn có thể kiểm soát tất cả các chương trình có liên quan, tần suất thay thế là không cao, bạn có thể làm như sau.

Để mở/đọc file:

  1. Open "resource.txt", nếu có hỏng hóc
  2. Open "resource.old.txt", nếu có hỏng hóc
  3. Open "resource.txt "một lần nữa, nếu điều đó không thành công
  4. Bạn có điều kiện lỗi.

Để thay thế các file:

  1. Rename "resource.txt" thành "resource.old.txt", sau đó
  2. Rename "resource.new.txt" thành "resource.txt" , sau đó
  3. Xóa "resource.old.txt".

Việc này sẽ đảm bảo tất cả người đọc của bạn luôn tìm thấy tệp hợp lệ.

Nhưng, dễ dàng hơn, sẽ chỉ đơn giản là cố gắng mở của bạn trong một vòng lặp, như:

InputStream inp=null; 
StopWatch tmr=new StopWatch();      // made up class, not std Java 
IOException err=null; 

while(inp==null && tmr.elapsed()<5000) {    // or some approp. length of time 
    try { inp=new FileInputStream("resource.txt"); } 
    catch(IOException thr) { err=thr; sleep(100); } // or some approp. length of time 
    } 

if(inp==null) { 
    // handle error here - file did not turn up after required elapsed time 
    throw new IOException("Could not obtain data from resource.txt file"); 
    } 

... carry on 
1

Bạn có thể nhận được một số lực kéo bằng cách thiết lập một khóa FileChannel vào file trước khi đổi tên nó (và xóa các tập tin bạn sẽ ghi đè lên một khi bạn có khóa). -r

30

Đối với Java 1.7+, hãy sử dụng java.nio.file.Files.move(Path source, Path target, CopyOption... options) với CopyOptions "REPLACE_EXISTING" và "ATOMIC_MOVE".

See API documentation for more information.

Ví dụ:

Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE); 
+12

Nếu bạn chỉ định ATOMIC_MOVE, thì "tất cả các tùy chọn khác sẽ bị bỏ qua" và "nếu tệp đích tồn tại thì nó sẽ được thực hiện cụ thể nếu tệp hiện có được thay thế hoặc phương thức này không thành công bằng cách ném một IOException". Tuy nhiên, tôi đã thử nghiệm vượt qua ATOMIC_MOVE trên Windows 7, Solaris 10 và RHEL Server 6.3 và tất cả chúng đều thực hiện đổi tên nguyên tử, thay thế tệp đích. –

+2

Tôi nên đề cập đến rằng tôi chỉ thử nghiệm đổi tên các tập tin (không phải thư mục) trong cùng một thư mục (không phải hệ thống tập tin chéo) trên hệ thống tập tin cục bộ (không nối mạng). Đó là đủ cho mục đích của tôi, nhưng YMMV. –

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