Question

I'm making a client server application in Java. In short, the Server has some files. The Client can send a file to the Server and the Client can request to download all the files from the Server. I'm using RMI for the Server and Client to communicate and I'm using the RMI IO library to send files between Client and Server.

Some example code:

Server:

class Server implements ServerService {

// private Map<String, File> files;
private ConcurrentHashMap<String, File> files // Solution

// adding a file to the server
public synchronized void addFile(RemoteInputStream inFile, String filename) 
    throws RemoteException, IOException {

    // From RMI IO library
    InputStream istream = RemoteInputStreamClient.wrap(inFile);
    File f = new File(dir, filename); 
    FileOutputStream ostream = new FileOutputStream(f);
    while (istream.available() > 0) {
        ostream.write(istream.read());
    }
    istream.close();
    ostream.close();
    files.put(filename, f);
}

// requesting all files
public requestFiles(ClientService stub) 
    throws RemoteException, IOException {

    for(File f: files.values()) {
        //Open a stream to this file and give it to the Client
        RemoteInputStreamServer istream = null; 
        istream = new SimpleRemoteInputStream(new BufferedInputStream(
                new FileInputStream(f)));
        stub.receiveFile(istream.export());
    }
}

Please note that this is just some example code to demonstrate.

My questions concerns concurrent access to the files on the Server. As you can see, I've made the addFile method synchronized because it modifies the resources on my Server. My requestFiles method is not synchronized.

I am wondering if this can cause some trouble. When Client A is adding a File and Client B is at the same time requesting all files, or vice versa, will this cause trouble? Or will the addFile method wait (or make the other method wait) because it is synchronized?

Thanks in advance!

Was it helpful?

Solution

Yes this could cause trouble. Other threads could access requestFiles(), whilst a single thread is performing the addFile() method.

It is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

[Source] http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

So methods that are declared syncronised lock the instance to all syncronised methods in that instance (In your case the instance of Server). If you had the requestFiles() method syncronised as well, you would essentially be syncronising access to the Server instance completely. So you wouldn't have this problem.

You could also use syncronised blocks on the files map. See this stackoverflow question: Java synchronized block vs. Collections.synchronizedMap


That being said, a model that essentially locks the entire Server object whenever a file is being written or read is hampering a concurrent design.

Depending on the rest of your design and assuming each file you write with the 'addFile()' method has a different name, and you are not overwriting files. I would explore something like the following:

Remove the map completely, and have each method interact with the file system separately.

I would use a temporary (.tmp) extension for files being written by 'addFile()', and then (once the file has been written) perform an atomic file rename to convert the extension to a '.txt' file.

Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);

Then restrict the entire 'requestFiles()' method to just '.txt' files. This way file writes and file reads could happen in parallel.

Obviously use whatever extensions you require.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top