2010-08-04 2 views
2

파일에 내 '데이터'개체를 직렬화하는 여러 스레드가 있습니다. 파일 이름은 개체VM 내의 개별 스레드에 대한 파일 잠금

 
    class Data { 
    org.joda.DateTime time; 
    String title; 

    public String getFilename() { 
     return time.toString() + '_' + title + ".xml"; 
    } 

2 데이터 객체가 같은 '시간'과 '제목'등 같은 파일 이름이있을 것이다 가능성이에서이 개 분야를 기반으로합니다.

이것은 받아 들일 수 있으며, 나는 구원 받았다. (그것들은 아마 같은 Data 객체 일 겁니다.)

두 개 이상의 스레드가 같은 시간에 파일에 쓰고 잘못된 형식의 XML을 생성했습니다.

필자는 java.nio.channels.FileLock을 살펴 봤지만 VM-Wide 잠금 용이며 특히 스레드 내 잠금에 적합하지 않습니다.

DataIO.class에서 동기화 할 수는 있지만 (실제로는 개별 파일을 동기화하기 때문에 엄청난 오버 헤드가 발생합니다).

여러 File 객체가 동일한 시스템 파일을 나타낼 수 있기 때문에 File 객체를 동기화하면 쓸모가 없습니다.

코드는 다음과 같습니다

 
class DataIO { 
    public void writeArticleToFile(Article article, String filename, boolean overwrite) throws IOException { 
    File file = new File(filename); 
    writeArticleToFile(article, file, overwrite); 
    } 

    public void writeDataToFile(Data data, File file, boolean overwrite) throws IOException { 
    if (file.exists()) { 
     if (overwrite) { 
     if (!file.delete()) { 
      throw new IOException("Failed to delete the file, for overwriting: " + file); 
     } 
     } else { 
     throw new IOException("File " + file + " already exists, and overwrite flag is set to false."); 
     } 
    } 

    File parentFile = file.getParentFile(); 
    if (parentFile != null) { 
     file.getParentFile().mkdirs(); 
    } 

    file.createNewFile(); 

    if (!file.canWrite()) { 
     throw new IOException("You do not have permission to write to the file: " + file); 
    } 

    FileOutputStream fos = new FileOutputStream(file, false); 
    try { 
     writeDataToStream(data, fos); 
     logger.debug("Successfully wrote Article to file: " + file.getAbsolutePath()); 
    } finally { 
     fos.close(); 
    } 
    } 
} 

답변

1

당신 수 인턴() 파일 이름 인 문자열입니다. 그런 다음 인턴 된 문자열을 동기화하십시오.

class DataIO { 
    public void writeArticleToFile(Article article, String filename, boolean overwrite) throws IOException { 
    synchronized(filename.intern()) { 
     File file = new File(filename); 
     writeArticleToFile(article, file, overwrite); 
    } 
    } 
+0

확실히 쉬운 해결책입니다. 잠금을 해제 한 객체가 공개적으로 액세스 할 수 없도록하는 것이 가장 좋습니다. (그리고 인턴 된 문자열은 언제 어디서나 사용할 수 있습니다) –

+0

이것은 사실이고 좋은 습관입니다. 그러나 문자열 자체는 DataIO 클래스에서 잠금의 기초를 형성하기 위해 문자열을 사용하여 더 이상 문자열을 잠그지 않습니다. 수정 된 게시물을 참조하십시오. –

+0

정말 정말 깔끔한 해결책이지만 Kirk Woll이 말한 것처럼 다른 곳에서는 String을 필요로 할 수 있다고 말합니다 (특히 파일 읽기 등). 그러나 파일 이름 앞에 접두사가 붙은 String (정규화 된 클래스 이름 일 수 있음)을 붙인 다음 THAT String의 intern()을 잠그면 해당 String 객체를 필요로 할 확률은 거의 0이됩니다. 나에게. – barryred

2

정확하게 읽는다면 하나의 파일을 나타내는 데이터 개체가 있습니다.

데이터 객체를 기반으로 스트라이프 세트를 만들 수 있습니다. 당신은 당신이 할 수있는이 객체에 기록 할 때 아마도

ConcurrentMap<Data,Lock> lockMap = new ConcurrentHashMap<Data,Lock>(); 

없음의 ConcurrentHashMap의를 갖는

Lock lock = lockMap.get(someMyDataObject); 
lock.lock(); 
try{ 
    //write object here 
}finally{ 
    lock.unlock(); 
} 

당신이 해시 코드를 작성해야 명심하고 제목을 기반 방법과 동일하고 DateTime

0

나는 동기화를 사용하는 것이 당신이 사용해야하는 기술이라는 것에 동의합니다. 당신이 필요로하는 것은 각 파일 순열을위한 개별 객체이며, 더 중요한 것은 매번 동일한 객체입니다.

public class FileLock { 
    DateTime time; 
    String title; 

    public FileLock(DateTime time, String title) { 
     this.time = time; 
     this.title = title; 
    } 

    override equals/hashCode based on those two properties 

    static Hashtable<FileLock, FileLock> unqiueLocks = new Hashtable<FileLock, FileLock>(); 
    static lockObject = new Object(); 

    public static FileLock getLock(DateTime time, String title) { 
     synchronized (lockObject) { 
      FileLock lock = new FileLock(time, title); 
      if (unqiueLocks.ContainsKey(lock)) { 
       return unqiueLocks.get(lock); 
      } 
      else { 
       unqiueLocks.put(lock, lock); 
       return lock; 
      } 
     } 
    } 
} 

그런 다음 발신자처럼 사용한다 : 하나의 옵션은 클래스라는 FileLock를 만들 수 있습니다

synchronized (FileLock.getLock(time, title)) { 
    ... 
} 

Hashtable의 새로운 파일/시간이 계속 성장하기 때문에이 메모리 누수가 마음에 곰 순열. 필요한 경우이 기술을 수정하여 getLock의 호출자가 Hashtable을 깨끗하게 유지하는 데 사용하는 releaseLock 메소드도 호출 할 수 있습니다.