我有一个使用位于特定目录中的索引的方法。
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()方法,所以我假设在方法上设置synchronize将解决这个问题,但是如果当线程已经在其中时目录变坏了,那么它们都会开始调用rebuild方法同时?换句话说,我对在多线程环境中删除和更新IO文件的正确方法感到有些困惑。一些建议将不胜感激。
答案 0 :(得分: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;
}
答案 1 :(得分:0)
IndexWriter已经是线程安全的,因此您可以使用来自不同线程的相同实例。
如果没有,请考虑在单个线程中执行所有索引操作。使用java ExecutorService很容易做到。