最近有个项目要用solr,solr是基于lucene的,今天在测试indexwriter时遇到了lock的问题:

测试代码:

import java.io.File;
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
public class TestLock {
    private Directory dir;
    public static TestLock ttt;
    public static IndexWriter writer2;
    private Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_48);
    private IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_48, analyzer);
    public void init() throws Exception {
        String pathFile = "D://luceneindex";
        try{
               dir = FSDirectory. open(new File(pathFile));
        } catch (IOException e) {        
            throw new RuntimeException(e);
        }
        IndexWriter writer = getWriter();             
        System. out.println("init ok,test IndexWriter lock" );
        LockTest( writer);      
        //writer.close();
    }
    public IndexWriter getWriter() throws Exception {
        //analyzer.close();
        return new IndexWriter(dir, iwc);
    }
    public void LockTest(IndexWriter w1) {
       try{
               if(w1.isLocked (dir )){
                     System. out.println("write1 locked" );
                     IndexWriterConfig iwc1 = new IndexWriterConfig(Version.LUCENE_48 , analyzer );
                      writer2 = new IndexWriter(dir, iwc1); 
              } else{
                     System. out.println("write1 not locked" );                      
              }      
       } catch(Exception e){
              e.printStackTrace();
       }       
    }  
    public static void main(String[] args){
       ttt = new TestLock();
       try{
               ttt.init();
       } catch(Exception e){
              e. printStackTrace();              
       }      
    }
}

报错信息:

org.apache.lucene.store.LockObtainFailedException : Lock obtain timed out: NativeFSLock@D:\luceneindex\write.lock: java.nio.channels.OverlappingFileLockException
       at org.apache.lucene.store.Lock.obtain( Lock.java:89)
       at org.apache.lucene.index.IndexWriter.<init>( IndexWriter.java:710)
       at TestLock.LockTest( TestLock.java:38)
       at TestLock.init( TestLock.java:26)
       at TestLock.main( TestLock.java:51)
Caused by: java.nio.channels.OverlappingFileLockException
       at sun.nio.ch.SharedFileLockTable.checkList(Unknown Source)
       at sun.nio.ch.SharedFileLockTable.add(Unknown Source)
       at sun.nio.ch.FileChannelImpl.tryLock(Unknown Source)
       at java.nio.channels.FileChannel.tryLock(Unknown Source)
       at org.apache.lucene.store.NativeFSLock.obtain(NativeFSLockFactory.java:148)
       at org.apache.lucene.store.Lock.obtain( Lock.java:100)
       ... 4 more

从错误信息可以看到是IndexWriter获取锁出错导致,从堆栈信息可以看到,是在运行IndexWriter的构造方法时,涉及到锁的操作。

查看IndexWriter的相关源码:

doc:
Opening an IndexWriter creates a lock file for the directory in use. Trying to open another IndexWriter  on the same directory will lead to a
LockObtainFailedException. The  LockObtainFailedException is also thrown if an IndexReader on the same directory is used to delete documents
from the index.

两个和锁相关的属性:

  public static final String WRITE_LOCK_NAME = "write.lock" ;
  private Lock writeLock;

在IndexWriter的构造函数中:

  public IndexWriter(Directory d, IndexWriterConfig conf) throws IOException
....
     directory = d;
.....
    writeLock = directory.makeLock(WRITE_LOCK_NAME);
    if (! writeLock.obtain(config .getWriteLockTimeout())) // obtain write lock(尝试获取锁)
      throw new LockObtainFailedException("Index locked for write: " + writeLock );

其中Lock是一个抽象类(org.apache.lucene.store.Lock):   

锁的获取主要实现是在obtain方法:

  public static long LOCK_POLL_INTERVAL = 1000;
  public static final long LOCK_OBTAIN_WAIT_FOREVER = -1;
  public final boolean obtain(long lockWaitTimeout) throws IOException {
    failureReason = null;
    boolean locked = obtain();
    if (lockWaitTimeout < 0 && lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER )
      throw new IllegalArgumentException("lockWaitTimeout should be LOCK_OBTAIN_WAIT_FOREVER or a non-negative number (got " + lockWaitTimeout + ")");
    long maxSleepCount = lockWaitTimeout / LOCK_POLL_INTERVAL;
    long sleepCount = 0;
    while (!locked) {
      if (lockWaitTimeout != LOCK_OBTAIN_WAIT_FOREVER && sleepCount++ >= maxSleepCount) {
        String reason = "Lock obtain timed out: " + this .toString();
        if (failureReason != null) {
          reason += ": " + failureReason ;
        }
        LockObtainFailedException e = new LockObtainFailedException(reason);
        if (failureReason != null) {
          e.initCause( failureReason);
        }
        throw e;
      }
      try {
        Thread. sleep(LOCK_POLL_INTERVAL);
      } catch (InterruptedException ie) {
        throw new ThreadInterruptedException(ie);
      }
      locked = obtain();
    }
    return locked;
  }

可以看到有由下面几个因素决定:

1.lockWaitTimeout 超时时间(总时间,超过这个时间即timeout),默认1000ms

2.LOCK_OBTAIN_WAIT_FOREVER  是否无限获取(-1),如果设置为-1,会永不超生

3.LOCK_POLL_INTERVAL  重试间隔时间,默认1000ms

在关闭IndexWriter时调用close方法(实现了Closeable接口的类都会有close方法)即可正常释放锁

更改代码为如下即可:

  public void LockTest(IndexWriter w1) {
       try{
               if(w1.isLocked (dir )){
                     System. out.println("write1 locked" );
                     w1.close();
                     IndexWriterConfig iwc1 = new IndexWriterConfig(Version.LUCENE_48 , analyzer );
                      writer2 = new IndexWriter(dir, iwc1); 
              } else{
                     System. out.println("write1 not locked" );                      
              }      
       } catch(Exception e){
              e.printStackTrace();
       }       
    }

另外关于lock的判断和unlock方法如下:

 public static boolean isLocked(Directory directory) throws IOException {  // 判断写目录是否被lock
    return directory.makeLock( WRITE_LOCK_NAME).isLocked();
  }
  /**
   * Forcibly unlocks the index in the named directory.
   * <P>
   * Caution: this should only be used by failure recovery code,
   * when it is known that no other process nor thread is in fact
   * currently accessing this index.
   */
  public static void unlock(Directory directory) throws IOException { // 解锁
    directory.makeLock(IndexWriter. WRITE_LOCK_NAME).close();
  }