Soru Bir dosya zaten var olsa bile, Java'da bir dosyayı atomik olarak nasıl yeniden adlandırabilirim?


Her biri bir Java uygulaması çalıştıran bir makineler kümem var.

Bu Java uygulamalarının benzersiz bir şeye erişmesi gerekiyor resource.txt tutarlı biçimde dosya.

Atomik olarak adlandırmam gerekiyor temp.txt dosyaya resource.txt Java’da bile resource.txt zaten var.

silme resource.txt ve yeniden adlandırma temp.txt atomik olmadığı için işe yaramıyor (küçük bir zaman dilimi yaratıyor) resource.txt mevcut değil).

Ve çapraz platform olmalı ...

Teşekkürler !


32
2018-02-27 16:59


Menşei


Kendime, 8 yıl sonra not: Ben bağlamı hatırlamıyorum, ama eğer sorun platformlar arası atomiklik gerektirir, belki çözüm dosya içermemeli mi? - Sébastien RoccaSerra


Cevaplar:


Java 1.7+ için java.nio.file.Files.move(Path source, Path target, CopyOption... options) CopyOptions "REPLACE_EXISTING" ve "ATOMIC_MOVE" ile.

Daha fazla bilgi için API belgelerine bakın.

Örneğin:

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

31
2018-05-10 10:49



ATOMIC_MOVE belirtirseniz, "tüm diğer seçenekler göz ardı edilir" ve "hedef dosya varsa, varolan dosya değiştirilirse veya bir IOException atanarak bu yöntem başarısız olursa, uygulamaya özgüdür". Ancak, Windows 7, Solaris 10 ve RHEL Server 6.3 üzerinde ATOMIC_MOVE geçtiğini test ettim ve hepsi hedef dosyayı değiştirerek, atomik olarak yeniden adlandırın. - Martin McNulty
Yerel (ağa bağlı olmayan) dosya sistemlerinde aynı dizinde (çapraz dosya sistemi değil) dosyaları yeniden adlandırmayı (dizinleri değil) denediğimi belirtmeliyim. Amaçlarım için yeterli, ama YMMV. - Martin McNulty


Linux'ta (ve Solaris ve diğer UNIX işletim sistemlerine inanıyorum), Java'nın File.renameTo () yöntemi var ise hedef dosyanın üzerine yazacaktır, ancak bu Windows altında geçerli değildir.

Çapraz platform olmak için, resource.txt'de dosya kilitlemeyi kullanmanız ve ardından verilerin üzerine yazmanız gerektiğini düşünüyorum.

Dosya kilidinin davranışı   platforma göre. Bazı platformlarda,   dosya kilidi tavsiye, yani   Bir uygulama kontrol etmedikçe   bir dosya kilidi, bu engellenmeyecek   dosyaya erişmekten Diğer üzerinde   platformlar, dosya kilidi zorunludur,   Yani bir dosya kilidi engeller   herhangi bir uygulamaya erişim   dosya.

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

    // 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
    lock.release();

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

Windows varsayılan olarak Linux, varsayılan olarak, gönüllü kilitleme kullanır. Belki işletim sistemini algılayabilir ve Windows için bazı kilitleme kodu ile UNIX altında renameTo () kullanabilirsiniz?

Belirli dosyalar için Linux altında zorunlu kilitlemeyi açmanın bir yolu da var, ama bu bir tür karanlık. Mod bitlerini doğru şekilde ayarlamanız gerekir.

Linux, Sistem V'yi takip ederek (bkz. Sistem   V Arabirim Tanımlaması (SVID) Sürümü   3), dosyalar için sgid biti verir   Grup yürütme izni yok   zorunlu kilitleme için dosya


13
2018-02-27 17:06



Yeni yol java.nio olması gerektiğinden, bu kullanımdan kaldırılmış olarak işaretlenmelidir! - user1052080
Bu sadece dosya bir yazım işleminde üzerine yazılabilmesi için çok küçükse çalışır. - Thomas Mueller
@ user1052080 Ne yazık ki NIO, bu problemi çözmek için sadece kısmi olarak gider. NIO, bir akıştan yazma dışındaki dosya işlemlerinin senkronizasyonunu desteklemez. Bu bazen kritik bir konudur. - Leliel


İşte bir tartışma: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4017593


6
2018-02-27 17:10





Belirtildiği gibi İşteWindows işletim sistemi daha eski sürümler için atomik dosya yeniden adlandırmayı bile desteklemiyor gibi görünüyor. Bazı manuel kilitleme mekanizmalarını veya bir çeşit işlemi kullanmanız büyük olasılıktır. Bunun için bir göz atmak isteyebilirsiniz. apache commons işlemi paketi.


2
2018-02-27 18:11





Bu platformlar arası bir platform olmalıysa 2 seçenek öneririm:

  1. Tüm dosya erişimlerinden sorumlu bir ara hizmet gerçekleştirin. Burada istekleri senkronize etmek için çeşitli mekanizmalar kullanabilirsiniz. Her istemci java uygulaması, dosyaya yalnızca bu hizmet üzerinden erişir.
  2. Oluşturmak kontrol senkronize işlemleri gerçekleştirmek için gereken her zaman dosya. Dosyaya erişen her java uygulaması, kontrol dosya ve bunu beklerken kontrol dosya var. (neredeyse bir semafor gibi). Sil / yeniden adlandırma işlemini yapan süreç, oluşturma / silme işleminden sorumludur. kontrol dosya.

1
2018-02-27 17:29



Dosyayı Java dışında el ile silme hakkında ne düşünüyorsunuz? Tüm dosya erişiminin Java servisinden geçtiğini kontrol edemezsiniz. - TofuBeer
Lütfen bir kontrol dosyası kullanarak güvenli dosya kilitleme uygulamak için yeterli olmadığını unutmayın! - user3151902


Yeniden adlandırmanın amacı, resource.txt dosyasını anında değiştirmektir. ve ilgili tüm programlar üzerinde kontrol sahibi olursunuz, ve Değiştirme sıklığı yüksek değil, aşağıdakileri yapabilirsiniz.

Dosyayı açmak / okumak için:

  1. Bu başarısız olursa "resource.txt" dosyasını açın
  2. Bu başarısız olursa "resource.old.txt" dosyasını açın
  3. Bu başarısız olursa "resource.txt" i tekrar aç
  4. Bir hata durumunuz var.

Dosyayı değiştirmek için:

  1. "Resource.txt" dosyasını "resource.old.txt" olarak yeniden adlandırın, ardından
  2. "Resource.new.txt" dosyasını "resource.txt" olarak yeniden adlandırın, ardından
  3. "Resource.old.txt" öğesini silin.

Tüm okuyucularınızın her zaman geçerli bir dosya bulmasını sağlayacaktır.

Ancak, daha kolay, basitçe açılışınızı bir döngü içinde denemek, örneğin:

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
2018-03-01 03:57





Dosyayı yeniden adlandırmadan önce dosyada bir filechannel kilidi oluşturarak biraz çekiş yapabilirsiniz (ve kilitlendikten sonra üzerine yazacağınız dosyayı silebilirsiniz). -r


1
2018-02-22 21:10