5

I have more then 3 java processes accessing same file for read and write. Each process has multiple threads that read and write file very frequently (at the rate of 10 times or so in 1 second).

I am using java.nio.channels.FileLock for interprocess file locking. And commonObj.wait() commonObj.notify() for interthread synchronization.

The issue I am facing in this implementation is -

  1. java.io.IOException: Resource deadlock avoided exception occurs in one of the process.
  2. One of the process's file reader thread gets empty file may be because some other thread or process is writing file.

My question is,

  1. if thread releases file lock as soon as read or write is done then why issue 1 occurs ?
  2. if file is locked by each thread of all the processes before read or write then why does 2 issue occurs ?

I have written common reader writer classes for all java processes. Attaching the same.

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 {
   
 final static Logger logger = Logger.getLogger(JsonReader.class);
 static final ReentrantLock relock = new ReentrantLock();
      
 /**
  * Read given file in JSONObject 
  * @param fileName String
  * @return JSONObject
  */
    @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
        {   
         while(!islocked)
         {
          try
          {
           File file = new File(fileName);
           channel = new RandomAccessFile(file, "rw").getChannel();
           
           lock = channel.lock();
           if(lock != null)
           {
            islocked = true;
            fileReader = new FileReader(fileName);
            createdJsonObj = (JSONObject) jsonParser.parse(fileReader);
           }
          }
          catch(OverlappingFileLockException e)
          {
           logger.error("FILE LOCK OVERLAP EXP OCCURED IN READING FILE " + fileName
               +". ATTEMPTING TO READ FILE AGAIN.");
           //Thread.sleep(1);
           //release the lock
           if(lock != null)
           {
            lock.release();
           }
           // close the channel
           if(channel != null)
           {
            channel.close();
           }
           synchronized (relock) {
         relock.wait();
        }
          }
         } //while
        }
        catch (FileNotFoundException e)
        {
         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)
        {
         e.printStackTrace();
         logger.error("PARSING ERROR IN JSON FOR FILE NAMED "+fileName+".",e);
        }
        catch (Exception e)
        {
         e.printStackTrace();
         logger.error("ERROR IN JSON FOR FILE NAMED "+fileName+".",e);           
        }
        finally {
            try {
                if(fileReader != null)
                {
                 fileReader.close();
                }
                // release the lock
                if(lock != null)
                    lock.release();
                // close the channel
                if(channel != null)
                {
                 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 {
             synchronized (relock) {
        relock.notify();
       }
   }
        }
       
        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 {
   
 final static Logger logger = Logger.getLogger(JsonWriter.class);
      
 /**
  * Write given JSONObject into given file name
  * @param fileName String
  * @param ObjToWrite JSONObejct
  * @return boolean true on success else false
  */
    @SuppressWarnings("resource")
    public static boolean writeJsonFile(String fileName, JSONObject ObjToWrite) {
     
     boolean writeFlag = false;
     
        FileChannel channel = null;
        FileLock lock = null;
        FileWriter fileWriter = null;
        boolean islocked = false;
        
        try
        {            
         while(!islocked)
         {
          try
          {
           File file = new File(fileName);
           channel = new RandomAccessFile(file, "rw").getChannel();
           lock = channel.lock();
           
           if(lock != null)
           {
            islocked = true;
            fileWriter = new FileWriter(fileName);
            Gson gson2 = new GsonBuilder().setPrettyPrinting().create();
                        String json2 = gson2.toJson(ObjToWrite);
                        fileWriter.write(json2);
            writeFlag = true;            
           }
          }
          catch(OverlappingFileLockException e)
          {
           logger.error("FILE LOCK OVERLAP EXP OCCURED IN WRITING FILE " + fileName
               +". ATTEMPTING TO WRITE FILE AGAIN.");
           
           //release the lock
           if(lock != null)
           {
            lock.release();
           }
           // close the channel
           if(channel != null)
           {
            channel.close();
           }
           synchronized (JsonReader.relock) {
            JsonReader.relock.wait();
        }
          }
         }
        }
        catch (FileNotFoundException e)
        {
         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)
        {
         e.printStackTrace();
         logger.error("ERROR IN JSON FOR FILE NAMED "+fileName+".",e);           
        }
        finally {
            try {
                if(fileWriter != null)
                {
                 fileWriter.flush();
                 fileWriter.close();
                }
                // release the lock
                if(lock != null)
                    lock.release();
                // close the channel
                if(channel != null)
                {
                 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 {
             synchronized (JsonReader.relock) {
              JsonReader.relock.notify();
       }
   }
        }       
        return writeFlag;
    }
}
code_poetry
  • 333
  • 1
  • 6
  • 16

1 Answers1

1

I think you are running this program on linux. The java will be using (mostly) POSIX lock http://www.man7.org/linux/man-pages/man2/fcntl.2.html

See the part where the manual mentions about EDEADLK. The linux OS is most probably unable to identify that 2 different threads are running inside the same JVM. see a similar example in https://gist.github.com/harrah/4714661 here.

prembhaskal
  • 425
  • 1
  • 4
  • 10