Mr.Nois
Mr.Nois

Reputation: 35

How to achieve Synchronization in Multithreading Java App

I've designed a Server-Client App using Java and i have connected to the Server more than one users. The Server provides some features, such as:


I spotted some issues that need to be synchronized when two or more users send the same request.

For Example: When users want to download the same file at the same time, how can i synchronize this action by using synchronized blocks or any other method?

 //main (connecting 2 users in the server)
 ServerSocket server= new ServerSocket(8080, 50);
 MyThread client1=new MyThread(server);
 MyThread client2=new MyThread(server);
 client1.start();
 client2.start();

Here is the method i would like to synchronize:

//outstream = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));//output to client
//instream = new BufferedReader(new InputStreamReader(sock.getInputStream()));//input from client

public  void downloadFile(File file,String name) throws FileNotFoundException, IOException {
    synchronized(this)
    {

    if (file.exists()) {
        BufferedReader readfile = new BufferedReader(new FileReader(name + ".txt"));


        String newpath = "../Socket/" + name + ".txt";

        BufferedWriter socketfile = new BufferedWriter(new FileWriter(newpath));
        String line;

        while ((line = readfile.readLine()) != null) {
            outstream.write(line + "\n");
            outstream.flush();
            socketfile.write(line);

        }
        outstream.write("EOF\n");
        outstream.flush();
        socketfile.close();
        outstream.write("Downloaded\n");
        outstream.flush();
    } else {
        outstream.write("FAIL\n");
    }
    outstream.flush();

  }
}

Note: This method is in a class that extends Thread and is being used when i want to "download" the file in the overriden method Run()


Does this example assures me that when 2 users want to download the same file one of them will have to wait? and will the other one get it? Thanks for your time!

Upvotes: 0

Views: 177

Answers (1)

Stepan Pogosyan
Stepan Pogosyan

Reputation: 572

Locking in concurrent is used to provide mutual exclusion to some piece of code. For locking you can use as synchronized as unstructured lock like ReentrantLock and others.

The main goal of any lock is to provide mutual exclusion to the piece of code placed inside which will mean that this piece will be executed only by one thead at a time. Section inside the lock is called critical section.

To achieve a proper locking it is not enough just to place critical code there. Also you have to make sure that modification of you variables made inside the critical section is made only there. Because if you locked some piece of code but references to variables inside also got passed to some concurrent executing thread without any locking then lock wont save you in that case and you will get a data race. Locks secure only execution of a critical section and only guarantee you that code placed in the critical section will be executed only by one thread at a time.

//outstream = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));//output to client //instream = new BufferedReader(new InputStreamReader(sock.getInputStream()));//input from client

public void downloadFile(File file,String name) throws FileNotFoundException, IOException { synchronized(this) {

Who is the owner of this method? Client? If yes then it won't work. You should lock on the same object. It should be shared with all threads which require locking. But in your case every client will have it's own lock and the other threads know nothing about other thread's locks. You can lock at the Client.class. This will work.

synchronize(this) vs synchronize(MyClass.class)

After doing that you will have proper locking for reading (downloading) the file. But what about write? Imagine the case when during reading some other thread will want to modify that file. But you have locks only for reading. You are reading the beginning of the file and the other thread is modifying the end of it. So the writing thread will succeed and you will get logically a corrupted file with the begging from one file and the end of the other. Of course file systems and standard java library try to take care about such cases (by using locks in readers\writes, locking the file offsets etc) but in general it is a possible scenario. So you will need also the same lock on write. And read and write methods should share and use the same lock.

And we've came to a situation when we have correct behavior but low performance. This is our tradeoff. But we can do better. Now we are using the same lock for every write and read method and this means that we can read or write to only one any file at a time. But this is not correct cause we can modify or read different files without any possible corruption. So the better approach will be to associate a lock with a file not the whole method. And here nio comes to help you.

How can I lock a file using java (if possible)

https://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileLock.html

And actually you can read a file concurrently if offsets are different. Due to obvious physical reasons you can't read the same part of a file concurrently. But concurrent read and taking care about offsets seems as a huge overhead fmpv and im not sure that you will need that. Anyway here is some info: Concurrent reading of a File (java preferred)

Upvotes: 1

Related Questions