Làm cách nào để khóa tệp bằng java (nếu có thể)


121

Tôi có một quy trình Java mở tệp bằng FileReader. Làm cách nào để ngăn quá trình (Java) khác mở tệp này, hoặc ít nhất là thông báo cho quá trình thứ hai rằng tệp đã được mở? Điều này có tự động làm cho quy trình thứ hai nhận được ngoại lệ nếu tệp đang mở (giải quyết vấn đề của tôi) hay tôi phải mở nó một cách rõ ràng trong quy trình đầu tiên với một số loại cờ hoặc đối số?

Làm rõ:

Tôi có một ứng dụng Java liệt kê một thư mục và mở từng tệp trong danh sách để xử lý nó. Nó xử lý từng tệp này đến tệp khác. Quá trình xử lý mỗi tệp bao gồm đọc nó và thực hiện một số tính toán dựa trên nội dung và mất khoảng 2 phút. Tôi cũng có một ứng dụng Java khác làm điều tương tự nhưng thay vào đó ghi trên tệp. Điều tôi muốn là có thể chạy các ứng dụng này cùng một lúc để tình huống diễn ra như thế này. ReadApp liệt kê thư mục và tìm các tệp A, B, C. Nó mở tệp A và bắt đầu đọc. WriteApp liệt kê thư mục và tìm các tệp A, B, C. Nó mở tệp A, thấy tệp đang mở (bằng một ngoại lệ hoặc bất kỳ cách nào) và chuyển đến tệp B. ReadApp kết thúc tệp A và tiếp tục đến B. Nó thấy rằng nó đang mở và tiếp tục đến C. Điều quan trọng là WriteApp không ' t ghi trong khi ReadApp đang đọc cùng một tệp hoặc ngược lại. Chúng là các quá trình khác nhau.


12
Bạn có nghĩa là 'quá trình' như trong quá trình (hai JVM) hay luồng (cùng một JVM). Tác động đến câu trả lời là tối quan trọng.
Stu Thompson

kiểm tra mã mẫu hiển thị giải pháp tại đây: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

Câu trả lời:


117

FileChannel.lock có thể là thứ bạn muốn.

try (
    FileInputStream in = new FileInputStream(file);
    java.nio.channels.FileLock lock = in.getChannel().lock();
    Reader reader = new InputStreamReader(in, charset)
) {
    ...
}

(Tuyên bố từ chối trách nhiệm: Mã không được biên dịch và chắc chắn không được kiểm tra.)

Lưu ý phần có tên "phụ thuộc nền tảng" trong tài liệu API cho FileLock .


22
Quan trọng hơn, hãy hiểu rằng khóa của JVM, và không phù hợp để khóa tệp để truy cập theo các luồng riêng lẻ trong một JVM duy nhất.
Stu Thompson

11
Bạn cần một luồng có thể ghi (tức là FileOutputStream).
Javier

@Javier Bạn có không? Tôi đã không cố gắng. Không có gì nhảy ra khỏi tài liệu API nói rằng đó là một yêu cầu. FileOutputStreamsẽ không được sử dụng nhiều cho một Reader.
Tom Hawtin - tackline

18
Có, tôi đã thử nó và nó ném NonWritableChannelException, vì lock()cố gắng có được một khóa độc quyền, nhưng điều đó yêu cầu quyền ghi. Nếu bạn có một luồng đầu vào , bạn có thể sử dụng luồng có lock(0L, Long.MAX_VALUE, false)được khóa chia sẻ và chỉ yêu cầu quyền truy cập đọc. Bạn cũng có thể sử dụng một RandomAccessFilemở ở chế độ đọc-ghi nếu bạn muốn có một khóa riêng trong khi đọc ... nhưng điều đó sẽ cấm người đọc đồng thời.
Javier

6
@Javier Tôi nghĩ bạn muốn nói lock(0L, Long.MAX_VALUE, true), không phải lock(0L, Long.MAX_VALUE, false). đối số cuối cùng ở đó là boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
john sullivan

60

Không sử dụng các lớp trong java.iogói, thay vào đó hãy sử dụng java.niogói. Sau này có một FileLocklớp. Bạn có thể áp dụng một khóa cho a FileChannel.

 try {
        // Get a file channel for the file
        File file = new File("filename");
        FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

        // Use the file channel to create a lock on the file.
        // This method blocks until it can retrieve the lock.
        FileLock lock = channel.lock();

        /*
           use channel.lock OR channel.tryLock();
        */

        // Try acquiring the lock without blocking. This method returns
        // null or throws an exception if the file is already locked.
        try {
            lock = channel.tryLock();
        } catch (OverlappingFileLockException e) {
            // File is already locked in this thread or virtual machine
        }

        // Release the lock - if it is not null!
        if( lock != null ) {
            lock.release();
        }

        // Close the file
        channel.close();
    } catch (Exception e) {
    }

btw, tôi đang viết trên tệp khóa pid hiện tại từ mẹo này stackoverflow.com/a/35885/1422630 , vì vậy sau khi tôi có thể đọc nó trên phiên bản mới!
Aquarius Power

1
cái này trông tốt, nhưng nó không hoạt động. Tôi nhận được OverlappingFileLockException mọi lúc, ngay cả khi tệp thậm chí không tồn tại
Gavriel

1
Sự cố sẽ xảy ra nếu Bạn gọi tryLock sau khi khóa vì nó được viết trong ví dụ
Igor Vuković

17

Nếu bạn có thể sử dụng Java NIO ( JDK 1.4 trở lên ), thì tôi nghĩ bạn đang tìmjava.nio.channels.FileChannel.lock()

FileChannel.lock ()


5
Có lẽ. Phụ thuộc vào ý nghĩa của OP bởi 'process'. "Khóa tệp được giữ thay mặt cho toàn bộ máy ảo Java. Chúng không phù hợp để kiểm soát quyền truy cập vào tệp theo nhiều luồng trong cùng một máy ảo."
Stu Thompson

@Stu: Tôi biết bạn đã trả lời câu hỏi này lâu lắm rồi, nhưng tôi hy vọng bạn có thể xây dựng những gì bạn có nghĩa là khi bạn nóiFile locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine
Thăng Phạm

3
@Harry Anh ấy đang trích dẫn từ tài liệu: download.oracle.com/javase/6/docs/api/java/nio/channels/… Nó có nghĩa là nó vô hình với các luồng nhưng ảnh hưởng đến các quy trình khác.
Artur Czajka

@Harry: Để thêm nhiều hơn nữa vào các nhận xét chưa xác định này, hãy tưởng tượng bạn đang sử dụng Java để phục vụ các trang web với Tomcat. Bạn có thể có nhiều chuỗi, mỗi chuỗi phục vụ một yêu cầu từ trình duyệt web. Tuy nhiên, tất cả chúng đều kiểm soát cùng một cơ chế khóa tệp giống như quá nhiều đầu bếp trong nhà bếp. Một yêu cầu có thể kết thúc ở giữa yêu cầu thứ hai và đột nhiên tệp của bạn được "mở khóa" trong khi bạn vẫn đang ở giữa một thứ gì đó và sau đó một số quy trình khác như cronjob có thể khóa nó, và sau đó bạn bất ngờ bị mất khóa và yêu cầu của bạn không thể hoàn thành ...
Darien


5

Đây có thể không phải là những gì bạn đang tìm kiếm, mà là sự quan tâm đến một vấn đề từ một góc độ khác ....

Hai quy trình Java này có thể muốn truy cập vào cùng một tệp trong cùng một ứng dụng không? Có lẽ bạn chỉ có thể lọc tất cả quyền truy cập vào tệp thông qua một phương pháp đồng bộ duy nhất (hoặc thậm chí tốt hơn là sử dụng JSR-166 )? Bằng cách đó, bạn có thể kiểm soát quyền truy cập vào tệp và có thể cả các yêu cầu truy cập hàng đợi.


3
Hai quy trình không thể sử dụng đồng bộ hóa, chỉ có hai luồng trong cùng một quy trình.
Marquis of Lorne,

3

Sử dụng RandomAccessFile, lấy kênh của nó, sau đó gọi lock (). Kênh được cung cấp bởi các luồng đầu vào hoặc đầu ra không có đủ đặc quyền để khóa đúng cách. Đảm bảo gọi unlock () trong khối cuối cùng (đóng tệp không nhất thiết phải giải phóng khóa).


bạn có thể xây dựng? Ý tôi là, khóa bằng Tệp truy cập ngẫu nhiên tốt hơn hoặc an toàn hơn so với luồng một
Paralife 25/09/08

Liên kết đến ví dụ đơn giản được đăng bên dưới
Touko

Paralife - xin lỗi vì sự chậm trễ - chỉ nhận thấy câu hỏi của bạn. Các khóa từ các luồng sẽ là các khóa đọc (đối với các luồng đầu vào) và các khóa ghi kênh đầy đủ, độc quyền (đối với các luồng đầu ra). Kinh nghiệm của tôi là các khóa từ RAF cho phép kiểm soát chi tiết hơn (tức là bạn có thể khóa các phần của tệp).
Ngày của Kevin,

1

Dưới đây là đoạn mã mẫu để khóa tệp cho đến khi xử lý xong bởi JVM.

 public static void main(String[] args) throws InterruptedException {
    File file = new File(FILE_FULL_PATH_NAME);
    RandomAccessFile in = null;
    try {
        in = new RandomAccessFile(file, "rw");
        FileLock lock = in.getChannel().lock();
        try {

            while (in.read() != -1) {
                System.out.println(in.readLine());
            }
        } finally {
            lock.release();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.