多线程以及多进程的Java文件锁定方式

问题描述:

我有更多的3个java进程访问相同的文件进行读写。每个进程都有多个线程,可以非常频繁地读取和写入文件(在1秒内以10次左右的速度读写)。多线程以及多进程的Java文件锁定方式

我正在使用java.nio.channels.FileLock进行进程间文件锁定。 和commonObj.wait()commonObj.notify()用于线程间同步。

我对着在此实现的问题是 - 在这个过程中的一个发生

  1. java.io.IOException: Resource deadlock avoided例外。
  2. 进程的文件读取器线程之一获取空文件可能是因为某些其他线程或进程正在写入文件。

我的问题是,

  1. 如果线程释放文件,只要阅读或问题的原因发生1写入,于是做了锁?
  2. 如果文件被读取前的所有进程的每个线程锁定或 写入然后为什么会发生2问题?

我已经为所有java进程编写了通用读写器类。附上相同的。

package json_file_handler; 
 

 
import java.io.File; 
 
import java.io.FileNotFoundException; 
 
import java.io.FileReader; 
 
import java.io.IOException; 
 
import java.io.RandomAccessFile; 
 
import java.nio.channels.FileChannel; 
 
import java.nio.channels.FileLock; 
 
import java.nio.channels.OverlappingFileLockException; 
 
import java.util.concurrent.locks.ReentrantLock; 
 

 
import org.apache.log4j.Logger; 
 
import org.json.simple.JSONArray; 
 
import org.json.simple.JSONObject; 
 
import org.json.simple.parser.JSONParser; 
 
import org.json.simple.parser.ParseException; 
 

 
public class JsonReader { 
 
    
 
\t final static Logger logger = Logger.getLogger(JsonReader.class); 
 
\t static final ReentrantLock relock = new ReentrantLock(); 
 
     
 
\t /** 
 
\t * Read given file in JSONObject 
 
\t * @param fileName String 
 
\t * @return JSONObject 
 
\t */ 
 
    @SuppressWarnings("resource") 
 
    public static JSONObject readJsonFile(String fileName) { 
 
     JSONObject createdJsonObj = null; 
 
     
 
     JSONParser jsonParser = new JSONParser(); 
 
     FileChannel channel = null; 
 
     FileLock lock = null; 
 
     FileReader fileReader = null; 
 
     boolean islocked = false; 
 
     
 
     try 
 
     { 
 
     \t while(!islocked) 
 
     \t { 
 
     \t \t try 
 
     \t \t { 
 
     \t \t \t File file = new File(fileName); 
 
     \t \t \t channel = new RandomAccessFile(file, "rw").getChannel(); 
 
     \t \t \t 
 
     \t \t \t lock = channel.lock(); 
 
     \t \t \t if(lock != null) 
 
     \t \t \t { 
 
     \t \t \t \t islocked = true; 
 
     \t \t \t \t fileReader = new FileReader(fileName); 
 
     \t \t \t \t createdJsonObj = (JSONObject) jsonParser.parse(fileReader); 
 
     \t \t \t } 
 
     \t \t } 
 
     \t \t catch(OverlappingFileLockException e) 
 
     \t \t { 
 
     \t \t \t logger.error("FILE LOCK OVERLAP EXP OCCURED IN READING FILE " + fileName 
 
     \t \t \t \t \t \t \t +". ATTEMPTING TO READ FILE AGAIN."); 
 
     \t \t \t //Thread.sleep(1); 
 
     \t \t \t //release the lock 
 
     \t \t \t if(lock != null) 
 
     \t \t \t { 
 
     \t \t \t \t lock.release(); 
 
     \t \t \t } 
 
     \t \t \t // close the channel 
 
     \t \t \t if(channel != null) 
 
     \t \t \t { 
 
     \t \t \t \t channel.close(); 
 
     \t \t \t } 
 
     \t \t \t synchronized (relock) { 
 
\t  \t \t \t \t relock.wait(); 
 
\t  \t \t \t } 
 
     \t \t } 
 
     \t } \t //while 
 
     } 
 
     catch (FileNotFoundException e) 
 
     { 
 
     \t e.printStackTrace(); 
 
      logger.error("FILE NOT FOUND ERROR IN READING JSON FOR FILE NAMED "+fileName+".",e); 
 
     } 
 
     catch (IOException e) 
 
     { 
 
      e.printStackTrace(); 
 
      logger.error("IO ERROR IN READING JSON FOR FILE NAMED "+fileName+".",e); 
 
     } 
 
     catch (ParseException e) 
 
     { 
 
     \t e.printStackTrace(); 
 
     \t logger.error("PARSING ERROR IN JSON FOR FILE NAMED "+fileName+".",e); 
 
     } 
 
     catch (Exception e) 
 
     { 
 
     \t e.printStackTrace(); 
 
     \t logger.error("ERROR IN JSON FOR FILE NAMED "+fileName+".",e);   
 
     } 
 
     finally { 
 
      try { 
 
       if(fileReader != null) 
 
       { 
 
       \t fileReader.close(); 
 
       } 
 
       // release the lock 
 
       if(lock != null) 
 
        lock.release(); 
 
       // close the channel 
 
       if(channel != null) 
 
       { 
 
       \t channel.close();    
 
       } 
 
      } 
 
      catch (IOException e) { 
 
       e.printStackTrace(); 
 
       logger.error("IO ERROR IN CLOSING FILE "+fileName+".",e); 
 
      } 
 
      catch (Exception e) { 
 
       e.printStackTrace(); 
 
       logger.error("ERROR IN CLOSING FILE "+fileName+".",e); 
 
      } 
 
      finally { 
 
      \t synchronized (relock) { 
 
    \t \t \t \t relock.notify(); 
 
    \t \t \t } 
 
\t \t \t } 
 
     } 
 
     
 
     return createdJsonObj; 
 
    } 
 
}

package json_file_handler; 
 

 
import java.io.File; 
 
import java.io.FileNotFoundException; 
 
import java.io.FileWriter; 
 
import java.io.IOException; 
 
import java.io.RandomAccessFile; 
 
import java.nio.channels.FileChannel; 
 
import java.nio.channels.FileLock; 
 
import java.nio.channels.OverlappingFileLockException; 
 

 
import org.apache.log4j.Logger; 
 
import org.json.simple.JSONArray; 
 
import org.json.simple.JSONObject; 
 

 
import com.google.gson.Gson; 
 
import com.google.gson.GsonBuilder; 
 

 
public class JsonWriter { 
 
    
 
\t final static Logger logger = Logger.getLogger(JsonWriter.class); 
 
     
 
\t /** 
 
\t * Write given JSONObject into given file name 
 
\t * @param fileName String 
 
\t * @param ObjToWrite JSONObejct 
 
\t * @return boolean true on success else false 
 
\t */ 
 
    @SuppressWarnings("resource") 
 
    public static boolean writeJsonFile(String fileName, JSONObject ObjToWrite) { 
 
    \t 
 
    \t boolean writeFlag = false; 
 
    \t 
 
     FileChannel channel = null; 
 
     FileLock lock = null; 
 
     FileWriter fileWriter = null; 
 
     boolean islocked = false; 
 
     
 
     try 
 
     {   \t 
 
     \t while(!islocked) 
 
     \t { 
 
     \t \t try 
 
     \t \t { 
 
     \t \t \t File file = new File(fileName); 
 
     \t \t \t channel = new RandomAccessFile(file, "rw").getChannel(); 
 
     \t \t \t lock = channel.lock(); 
 
     \t \t \t 
 
     \t \t \t if(lock != null) 
 
     \t \t \t { 
 
     \t \t \t \t islocked = true; 
 
     \t \t \t \t fileWriter = new FileWriter(fileName); 
 
     \t \t \t \t Gson gson2 = new GsonBuilder().setPrettyPrinting().create(); 
 
         String json2 = gson2.toJson(ObjToWrite); 
 
         fileWriter.write(json2); 
 
     \t \t \t \t writeFlag = true;   \t \t \t \t 
 
     \t \t \t } 
 
     \t \t } 
 
     \t \t catch(OverlappingFileLockException e) 
 
     \t \t { 
 
     \t \t \t logger.error("FILE LOCK OVERLAP EXP OCCURED IN WRITING FILE " + fileName 
 
     \t \t \t \t \t \t \t +". ATTEMPTING TO WRITE FILE AGAIN."); 
 
     \t \t \t 
 
     \t \t \t //release the lock 
 
     \t \t \t if(lock != null) 
 
     \t \t \t { 
 
     \t \t \t \t lock.release(); 
 
     \t \t \t } 
 
     \t \t \t // close the channel 
 
     \t \t \t if(channel != null) 
 
     \t \t \t { 
 
     \t \t \t \t channel.close(); 
 
     \t \t \t } 
 
     \t \t \t synchronized (JsonReader.relock) { 
 
     \t \t \t \t JsonReader.relock.wait(); 
 
\t  \t \t \t } 
 
     \t \t } 
 
     \t } 
 
     } 
 
     catch (FileNotFoundException e) 
 
     { 
 
     \t e.printStackTrace(); 
 
      logger.error("FILE NOT FOUND ERROR IN WRITING JSON FOR FILE NAMED "+fileName+".",e); 
 
     } 
 
     catch (IOException e) 
 
     { 
 
      e.printStackTrace(); 
 
      logger.error("IO ERROR IN WRITING JSON FOR FILE NAMED "+fileName+".",e); 
 
     } 
 
     catch (Exception e) 
 
     { 
 
     \t e.printStackTrace(); 
 
     \t logger.error("ERROR IN JSON FOR FILE NAMED "+fileName+".",e);   
 
     } 
 
     finally { 
 
      try { 
 
       if(fileWriter != null) 
 
       { 
 
       \t fileWriter.flush(); 
 
       \t fileWriter.close(); 
 
       } 
 
       // release the lock 
 
       if(lock != null) 
 
        lock.release(); 
 
       // close the channel 
 
       if(channel != null) 
 
       { 
 
       \t channel.close();    
 
       } 
 
      } 
 
      catch (IOException e) { 
 
       e.printStackTrace(); 
 
       logger.error("IO ERROR IN CLOSING FILE "+fileName+".",e); 
 
      } 
 
      catch (Exception e) { 
 
       e.printStackTrace(); 
 
       logger.error("ERROR IN CLOSING FILE "+fileName+".",e); 
 
      } 
 
      finally { 
 
      \t synchronized (JsonReader.relock) { 
 
      \t \t JsonReader.relock.notify(); 
 
    \t \t \t } 
 
\t \t \t } 
 
     }  
 
     return writeFlag; 
 
    } 
 
}

我认为你是在Linux上运行此程序。 java将使用(大部分)POSIX锁http://www.man7.org/linux/man-pages/man2/fcntl.2.html

查看手册提到的有关EDEADLK的部分。 linux操作系统很可能无法确定在同一个JVM中运行2个不同的线程。 在这里看到一个类似的例子https://gist.github.com/harrah/4714661