ฉันจะล็อคไฟล์โดยใช้ java ได้อย่างไร (ถ้าเป็นไปได้)


121

ฉันมีกระบวนการ Java ที่เปิดไฟล์โดยใช้ FileReader ฉันจะป้องกันไม่ให้กระบวนการอื่น (Java) เปิดไฟล์นี้ได้อย่างไรหรืออย่างน้อยก็แจ้งให้กระบวนการที่สองนั้นทราบว่าไฟล์นั้นเปิดอยู่แล้ว? สิ่งนี้ทำให้กระบวนการที่สองได้รับข้อยกเว้นโดยอัตโนมัติหรือไม่หากไฟล์เปิดอยู่ (ซึ่งช่วยแก้ปัญหาของฉันได้) หรือฉันต้องเปิดอย่างชัดเจนในกระบวนการแรกด้วยแฟล็กหรืออาร์กิวเมนต์บางประเภท?

ชี้แจง:

ฉันมีแอป Java ที่แสดงรายการโฟลเดอร์และเปิดแต่ละไฟล์ในรายการเพื่อประมวลผล มันประมวลผลแต่ละไฟล์หลังจากที่อื่น ๆ การประมวลผลของแต่ละไฟล์ประกอบด้วยการอ่านและทำการคำนวณบางอย่างตามเนื้อหาและใช้เวลาประมาณ 2 นาที ฉันยังมีแอป Java อื่นที่ทำสิ่งเดียวกัน แต่เขียนลงในไฟล์แทน สิ่งที่ฉันต้องการคือสามารถเรียกใช้แอพเหล่านี้ได้ในเวลาเดียวกันดังนั้นสถานการณ์จึงเป็นเช่นนี้ ReadApp แสดงรายการโฟลเดอร์และค้นหาไฟล์ A, B, C จะเปิดไฟล์ A และเริ่มการอ่าน WriteApp แสดงรายการโฟลเดอร์และค้นหาไฟล์ A, B, C มันเปิดไฟล์ A เห็นว่าเปิดอยู่ (ด้วยข้อยกเว้นหรือวิธีใดก็ได้) และไปที่ไฟล์ B ReadApp เสร็จสิ้นไฟล์ A และไปยัง B มันจะเห็นว่ามัน เปิดอยู่และไปยัง C. สิ่งสำคัญคือ WriteApp ไม่ ' t เขียนในขณะที่ ReadApp กำลังอ่านไฟล์เดียวกันหรือในทางกลับกัน เป็นกระบวนการที่แตกต่างกัน


12
คุณหมายถึง 'กระบวนการ' ในกระบวนการ (สอง JVMs) หรือเธรด (JVM เดียวกัน) ผลกระทบต่อคำตอบเป็นสิ่งสำคัญยิ่ง
Stu Thompson

ตรวจสอบโค้ดตัวอย่างที่แสดงโซลูชันที่นี่: stackoverflow.com/a/58871479/5154619
Davi Cavalcanti

คำตอบ:


117

FileChannel.lock น่าจะเป็นสิ่งที่คุณต้องการ

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

(ข้อจำกัดความรับผิดชอบ: โค้ดไม่ได้รวบรวมและไม่ผ่านการทดสอบ)

หมายเหตุ: ส่วนเรื่อง "การพึ่งพาแพลตฟอร์ม" ในเอกสาร API สำหรับ FileLock


22
ที่สำคัญกว่านั้นโปรดเข้าใจว่าการล็อกสำหรับ JVM และไม่เหมาะสำหรับการล็อกไฟล์เพื่อเข้าถึงโดยแต่ละเธรดภายใน JVM เดียว
Stu Thompson

11
คุณต้องมีสตรีมที่เขียนได้ (เช่นFileOutputStream)
Javier

@Javier ทำมั้ย? ฉันไม่ได้พยายาม ไม่มีสิ่งใดหลุดออกไปจากเอกสาร API ที่บอกว่าเป็นข้อกำหนด FileOutputStreamจะไม่เป็นประโยชน์สำหรับไฟล์Reader.
Tom Hawtin - แท็กไลน์

18
ใช่ฉันลองแล้วมันก็พ่นNonWritableChannelExceptionเพราะlock()พยายามที่จะได้มาซึ่งการล็อกแบบเอกสิทธิ์เฉพาะบุคคล แต่ต้องมีการเข้าถึงการเขียน หากคุณมีอินพุตสตรีมคุณสามารถใช้lock(0L, Long.MAX_VALUE, false)ซึ่งได้รับการล็อกที่ใช้ร่วมกันและต้องการการเข้าถึงแบบอ่านเท่านั้น นอกจากนี้คุณยังสามารถใช้การRandomAccessFileเปิดในโหมดอ่าน - เขียนได้หากคุณต้องการล็อคพิเศษขณะอ่าน ... แต่จะห้ามผู้อ่านพร้อมกัน
Javier

6
@Javier ฉันคิดว่าคุณหมายถึงว่าไม่lock(0L, Long.MAX_VALUE, true) lock(0L, Long.MAX_VALUE, false)อาร์กิวเมนต์สุดท้ายคือboolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…
john sullivan

60

อย่าใช้คลาสในไฟล์java.ioแพ็กเกจให้ใช้java.nioแพ็กเกจแทน ตอนหลังมีFileLockคลาส คุณสามารถใช้การล็อกกับไฟล์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 ฉันกำลังเขียนไฟล์ล็อค pid ปัจจุบันจากเคล็ดลับนี้stackoverflow.com/a/35885/1422630ดังนั้นหลังจากที่ฉันสามารถอ่านในอินสแตนซ์ใหม่ได้!
Aquarius Power

1
อันนี้ดูดี แต่ใช้ไม่ได้ ฉันได้รับ OverlappingFileLockException ทุกครั้งแม้ว่าไฟล์จะไม่มีอยู่ก็ตาม
Gavriel

1
ปัญหาจะเกิดขึ้นถ้าคุณเรียก tryLock หลังจากล็อคตามที่เขียนไว้ในตัวอย่าง
Igor Vuković

17

หากคุณสามารถใช้Java NIO ( JDK 1.4 หรือสูงกว่า ) ฉันคิดว่าคุณกำลังมองหาjava.nio.channels.FileChannel.lock()

FileChannel.lock ()


5
อาจจะ. ขึ้นอยู่กับความหมายของ OP โดย 'กระบวนการ' "การล็อกไฟล์จะจัดขึ้นในนามของเครื่องเสมือน Java ทั้งหมดซึ่งไม่เหมาะสำหรับการควบคุมการเข้าถึงไฟล์โดยหลายเธรดภายในเครื่องเสมือนเดียวกัน"
Stu Thompson

@ สตู: ฉันรู้ว่าคุณตอบคำถามนี้มานานแล้ว แต่ฉันหวังว่าคุณจะสามารถอธิบายได้อย่างละเอียดว่าคุณหมายถึงอะไรเมื่อคุณพูดFile 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
Thang Pham

3
@ แฮร์รี่เขาอ้างจากเอกสาร: download.oracle.com/javase/6/docs/api/java/nio/channels/…หมายความว่ามันมองไม่เห็นเธรด แต่ส่งผลกระทบต่อกระบวนการอื่น ๆ
Artur Czajka

@ แฮร์รี่: หากต้องการเพิ่มความคิดเห็นเหล่านี้ให้มากยิ่งขึ้นลองนึกภาพว่าคุณใช้ Java เพื่อให้บริการเว็บไซต์ด้วย Tomcat คุณอาจมีเธรดจำนวนมากโดยแต่ละชุดจะให้บริการหนึ่งคำขอจากเว็บเบราว์เซอร์ อย่างไรก็ตามพวกเขาทั้งหมดควบคุมกลไกการล็อกไฟล์เดียวกันเช่นพ่อครัวในครัวมากเกินไป คำขอหนึ่งอาจเสร็จสิ้นในช่วงวินาทีที่สองและทันใดนั้นไฟล์ของคุณก็ "ปลดล็อก" ในขณะที่คุณยังอยู่ในระหว่างทำอะไรบางอย่างจากนั้นกระบวนการอื่น ๆ เช่น cronjob อาจล็อกและจากนั้นคุณก็ทำหายโดยไม่คาดคิด ล็อคและคำขอของคุณไม่สามารถเสร็จสิ้น ...
Darien


5

นี่อาจไม่ใช่สิ่งที่คุณกำลังมองหา แต่เพื่อให้เกิดปัญหาจากอีกมุมหนึ่ง ....

กระบวนการ Java ทั้งสองนี้อาจต้องการเข้าถึงไฟล์เดียวกันในแอปพลิเคชันเดียวกันหรือไม่ บางทีคุณสามารถกรองการเข้าถึงไฟล์ทั้งหมดด้วยวิธีการซิงโครไนซ์เดียว (หรือดีกว่านั้นโดยใช้JSR-166 )? ด้วยวิธีนี้คุณสามารถควบคุมการเข้าถึงไฟล์และอาจถึงคิวการร้องขอการเข้าถึง


3
สองกระบวนการไม่สามารถใช้การซิงโครไนซ์มีเพียงสองเธรดในกระบวนการเดียวกัน
Marquis of Lorne

3

ใช้ RandomAccessFile รับมันเป็นช่องแล้วโทรล็อค () ช่องที่จัดเตรียมโดยสตรีมอินพุตหรือเอาต์พุตไม่มีสิทธิ์เพียงพอที่จะล็อกอย่างถูกต้อง อย่าลืมเรียกปลดล็อก () ในบล็อกสุดท้าย (การปิดไฟล์ไม่จำเป็นต้องคลายล็อก)


คุณสามารถอธิบายได้ไหม ฉันหมายถึงสิ่งที่ขยายออกไปคือการล็อคโดย RandomAccess File ดีกว่าหรือปลอดภัยกว่าสตรีมหนึ่ง
Paralife

ลิงก์ไปยังตัวอย่างง่ายๆที่โพสต์ด้านล่าง
Touko

Paralife - ขออภัยในความล่าช้า - เพิ่งสังเกตเห็นคำถามของคุณ การล็อกจากสตรีมจะถูกอ่านล็อก (สำหรับสตรีมอินพุต) และล็อกการเขียนแบบเต็มช่องสัญญาณแบบพิเศษ (สำหรับสตรีมเอาต์พุต) ประสบการณ์ของฉันคือการล็อคจาก RAF ช่วยให้สามารถควบคุมได้ละเอียดมากขึ้น (เช่นคุณสามารถล็อคบางส่วนของไฟล์)
Kevin Day

1

ด้านล่างนี้คือตัวอย่างโค้ดสำหรับล็อกไฟล์จนกว่า 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();
        }
    }

}

0

ใช้สิ่งนี้สำหรับ unix หากคุณกำลังโอนโดยใช้ winscp หรือ ftp:

public static void isFileReady(File entry) throws Exception {
        long realFileSize = entry.length();
        long currentFileSize = 0;
        do {
            try (FileInputStream fis = new FileInputStream(entry);) {
                currentFileSize = 0;
                while (fis.available() > 0) {
                    byte[] b = new byte[1024];
                    int nResult = fis.read(b);
                    currentFileSize += nResult;
                    if (nResult == -1)
                        break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("currentFileSize=" + currentFileSize + ", realFileSize=" + realFileSize);
        } while (currentFileSize != realFileSize);
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.