2012-03-23 35 views
1

我有一個使用位於特定目錄中的索引的方法。正在同步和IO文件

public class TestSearchEngine implements SearchEngine<Tag> { 

private static final String INDEX_PATH = "/test/index"; 

private Directory directory; 
@Inject private TagDAO tagDAO; 
private int organizationId; 

@Override 
public void add(Tag tag) { 
    IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35));   
    IndexWriter indexWriter = getIndexWriter(indexWriterConfig); 

    //Create document 
    Document document = new Document(); 
    document.add(new Field("id", String.valueOf(tag.getId()), Field.Store.YES, Field.Index.NOT_ANALYZED)); 
    document.add(new Field("title", tag.getTitle(), Field.Store.NO, Field.Index.ANALYZED)); 

    try { 
     indexWriter.addDocument(document); 
     indexWriter.close(); 
    } catch (CorruptIndexException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

@Override 
public synchronized void setDirectory(int organizationId) throws IOException { 
    this.organizationId = organizationId; 
    File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId)); 

    //If path does not exist, create it and create new index for organization 
    if(!path.exists()) { 
     path.mkdirs(); 
     buildCompleteIndex(organizationId, false); 
    } 

    this.directory = FSDirectory.open(path); //Open directory 
} 

private void buildCompleteIndex(int organizationId, boolean rebuildDir) { 
    if(rebuildDir) { 
     File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId)); 
     try { 
      Utils.deleteDirectory(path); 
     } catch (IOException e) { 
      throw new LuceneIndexException("Error rebuilding index directory.", e); 
     } 
     path.mkdirs(); 
    } 

    List<Tag> tagList = tagDAO.findAll(organizationId); 
    for(Tag tag : tagList) { 
     add(tag); 
    } 
} 

private IndexReader getIndexReader() { 
    try { 
     return IndexReader.open(directory); 
    } catch (CorruptIndexException e) { 
     buildCompleteIndex(organizationId, true); 
    } catch (IOException e) { 
     throw new LuceneIndexException("IOException prevented IndexReader from opening Index.", e); 
    } catch(NullPointerException e) { 
     throw new LuceneIndexException("Index resource not available.", e); 
    } 
    return null; 
} 

}

在某些情況下,我趕上如果出於某種原因該指數成爲腐敗的一個例外,或者它只是已尚未創建。在這種情況下,將調用buildCompleteIndex()方法,該方法刪除目錄並從數據源重新創建索引。

在多線程環境中,如何防止另一個線程創建類的實例,並使用調用該目錄時刪除或正在重建的方法? setDirectory()方法必須在任何其他方法工作之前調用,所以我假設在該方法上設置同步會解決這個問題,但是如果在線程已經存在的情況下該目錄變爲破壞,那麼它將如何開始調用重建方法在同一時間?換句話說,我對在多線程環境中刪除和更新IO文件的正確方法有點困惑。一些建議,將不勝感激。

回答

0

回顧你的代碼,你已經在setDirectory()上同步了。對於使用該特定實例的所有線程,它將阻塞,直到該方法完成。這包括buildCompleteIndex。如果你有創建這個類的新實例的線程,那麼你需要同步其他東西。例如,你可以同步一個靜態實例。

對文件和/或文件夾進行同步可能會變得非常複雜,特別是在運行多個環境(即JDK)的情況下。

像下面這樣,如果我理解你的問題可能的工作:

public class TestSearchEngine implements SearchEngine<Tag> { 

private static final String INDEX_PATH = "/test/index"; 

private Directory directory; 
@Inject private TagDAO tagDAO; 
private int organizationId; 

private static final Object mutex = new Object(); 

@Override 
public void add(Tag tag) { 
    IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35));   
    IndexWriter indexWriter = getIndexWriter(indexWriterConfig); 

    //Create document 
    Document document = new Document(); 
    document.add(new Field("id", String.valueOf(tag.getId()), Field.Store.YES, Field.Index.NOT_ANALYZED)); 
    document.add(new Field("title", tag.getTitle(), Field.Store.NO, Field.Index.ANALYZED)); 

    try { 
     indexWriter.addDocument(document); 
     indexWriter.close(); 
    } catch (CorruptIndexException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

@Override 
public void setDirectory(int organizationId) throws IOException { 

     synchronized (mutex) { 
     this.organizationId = organizationId; 
     File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId)); 

     //If path does not exist, create it and create new index for organization 
     if(!path.exists()) { 
      path.mkdirs(); 
      buildCompleteIndex(organizationId, false); 
     } 

     this.directory = FSDirectory.open(path); //Open directory 
     } 
} 

private void buildCompleteIndex(int organizationId, boolean rebuildDir) { 
    if(rebuildDir) { 
     File path = new File(INDEX_PATH + "/" + String.valueOf(organizationId)); 
     try { 
      Utils.deleteDirectory(path); 
     } catch (IOException e) { 
      throw new LuceneIndexException("Error rebuilding index directory.", e); 
     } 
     path.mkdirs(); 
    } 

    List<Tag> tagList = tagDAO.findAll(organizationId); 
    for(Tag tag : tagList) { 
     add(tag); 
    } 
} 

private IndexReader getIndexReader() { 
    try { 
     return IndexReader.open(directory); 
    } catch (CorruptIndexException e) { 
     buildCompleteIndex(organizationId, true); 
    } catch (IOException e) { 
     throw new LuceneIndexException("IOException prevented IndexReader from opening Index.", e); 
    } catch(NullPointerException e) { 
     throw new LuceneIndexException("Index resource not available.", e); 
    } 
    return null; 
} 
+0

所以我可以理解。互斥對象的要點是什麼?你能解釋一下怎麼工作嗎? – ryandlf 2012-03-23 19:36:42

+0

你必須有一些共同的東西來同步。你擁有它的方式會與「this」同步,這取決於創建實例的線程。使用靜態互斥鎖,所有實例都將與同一個實例同步,或者在這種情況下互斥鎖。也可能有更好的方法來實現這一點,但我認爲這將適用於你的用例。 – tjg184 2012-03-23 20:10:05

+0

好的...有道理。感謝您的解釋。 – ryandlf 2012-03-23 20:32:38

0

的IndexWriter已經是線程安全的,所以你如果有可能使用不同的線程相同的實例。

如果不是,請考慮在單個線程中執行所有索引操作。它很容易做到使用java ExecutorService