2012-03-23 11 views
1

Ich habe eine Methode, die einen Index in einem bestimmten Verzeichnis verwendet.Synchronisieren und IO-Datei

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; 
} 

}

In bestimmten Situationen fange ich eine Ausnahme, wenn aus irgendeinem Grunde der Index beschädigt wird, oder es einfach wurde noch nicht erstellt worden. In diesem Fall wird die Methode buildCompleteIndex() aufgerufen, die das Verzeichnis löscht und den Index aus einer Datenquelle neu erstellt.

Wie kann ich mich in einer Multithread-Umgebung gegen einen anderen Thread schützen, der eine Instanz der Klasse erstellt und eine Methode verwendet, die das Verzeichnis aufruft, während es gelöscht oder neu erstellt wird? Die setDirectory() -Methode muss aufgerufen werden, bevor eine der anderen Methoden funktionieren wird. Daher nehme ich an, dass die Synchronisierung mit der Methode das Problem löst. Wenn das Verzeichnis jedoch beschädigt wird, während Threads bereits darin enthalten sind, beginnen alle damit, die Neuerstellung aufzurufen Methode gleichzeitig? Mit anderen Worten, ich bin ein wenig verwirrt über die richtige Vorgehensweise zum Löschen und Aktualisieren von IO-Dateien in einer Multithread-Umgebung. Einige Ratschläge würden geschätzt werden.

Antwort

0

Überprüfen Sie Ihren Code, Sie haben auf setDirectory() synchronisiert. Für alle Threads, die diese bestimmte Instanz verwenden, wird sie blockiert, bis diese Methode abgeschlossen ist. Dies beinhaltet BuildCompleteIndex. Wenn Sie über Threads verfügen, die eine neue Instanz dieser Klasse erstellen, müssen Sie sie mit etwas anderem synchronisieren. Zum Beispiel könnten Sie mit einer statischen Instanz synchronisieren.

Das Synchronisieren von Dateien und Ordnern kann sehr kompliziert werden, besonders wenn Sie mehrere Umgebungen (d. H. JDKs) ausführen.

So etwas wie die folgenden funktionieren könnte, wenn ich Ihr Problem bin Verständnis:

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

So kann ich verstehen. Was ist der Sinn des Mutex-Objekts? Kannst du erklären, wie funktioniert? – ryandlf

+0

Sie müssen etwas haben, mit dem Sie synchronisieren können. Die Art, wie Sie es hatten, würde mit "this" synchronisiert, was davon abhängt, welcher Thread die Instanz erstellt hat. Mit einem statischen Mutex werden alle Instanzen mit derselben Instanz oder in diesem Fall mit Mutex synchronisiert. Es könnte auch bessere Möglichkeiten geben, dies zu implementieren, aber ich denke, das würde mit Ihrem Anwendungsfall funktionieren. – tjg184

+0

Ok ... macht Sinn. Danke für die Erklärung. – ryandlf

0

Indexwriter ist bereits Thread-sicher, so dass Sie, wenn möglich, die Verwendung der gleichen Instanz von verschiedenen Threads.

Wenn nicht, sollten Sie alle Indexmanipulationen in einem einzigen Thread durchführen. Es ist ziemlich einfach mit Java ExecutorService zu tun.

Verwandte Themen