Running threads in round robin fashion in java
Asked Answered
E

2

0

I am new to Multithreading and synchronization in java. I am trying to achieve a task in which i am given 5 files, each file will be read by one particular thread. Every thread should read one line from file then forward execution to next thread and so on. When all 5 threads read the first line, then again start from thread 1 running line no. 2 of file 1 and so on.

    Thread ReadThread1 = new Thread(new ReadFile(0));
    Thread ReadThread2 = new Thread(new ReadFile(1));
    Thread ReadThread3 = new Thread(new ReadFile(2));
    Thread ReadThread4 = new Thread(new ReadFile(3));
    Thread ReadThread5 = new Thread(new ReadFile(4));

    // starting all the threads
    ReadThread1.start();
    ReadThread2.start();
    ReadThread3.start();
    ReadThread4.start();
    ReadThread5.start();

and in ReadFile (which implements Runnable, in the run method, i am trying to synchronize on bufferreader object.

        BufferedReader br = null;

            String sCurrentLine;
            String filename="Source/"+files[fileno];
            br = new BufferedReader(new FileReader(filename));

            synchronized(br)
            {

            while ((sCurrentLine = br.readLine()) != null) {
                int f=fileno+1;
                System.out.print("File No."+f);
                System.out.println("-->"+sCurrentLine);
br.notifyAll();
// some thing needs to be dine here i guess 
}}

Need Help

Eskew answered 19/8, 2013 at 13:20 Comment(7)
You are entirely missing the wait part. But not at the spot you indicate; at the top of the run method.Paz
Sounds to me like you are looking for a Phaser - particularly look at the awaitPhase code in that link.Poetize
I don thin you can easily select the next thread to have access to the lock. Any way if you need to only have one thread running at the time why do you have thread... You should loop over the file to get each line.Actor
This is not really the sense of threads since they are meant to be parallel. So you could just create 5 classes instead of threads and assign those the ressource in a round robin style (e.g. in a while loop).Kenward
@Kenward This is a typical homework assignment problem. They have to do what they are asked to, whether it makes sense or not :)Paz
@Kenward I understand this, its a part of assignment, so that we can learn controlling thread execution. I can think of something like token-ring algorithm. pass the execution to next thread and wait for next turn.Eskew
I wish they could think of better homework questions :(Catawba
P
2

You are missing many parts of the puzzle:

  • you attempt to synchronize on an object local to each thread. This can have no effect and the JVM may even remove the whole locking operation;

  • you execute notifyAll without a matching wait;

  • the missing wait must be at the top of the run method, not at the bottom as you indicate.

Altogether, I'm afraid that fixing your code at this point is beyond the scope of one StackOverflow answer. My suggestion is to first familiarize yourself with the core concepts: the semantics of locks in Java, how they interoperate with wait and notify, and the precise semantics of those methods. An Oracle tutorial on the subject would be a nice start.

Paz answered 19/8, 2013 at 13:41 Comment(2)
I changed the object on which i need to synchronized. I took a class, and synchronized on its instance. But, still did not get how can i continue the execution in round robin.. I am able to do mutual exclusion and read one file completely by one thread, then start next threadEskew
You can use a static variable which will hold the number of the file which should be read next. You should then test this value in each thread for equality with the number you pass in as the constructor argument. This must happen in a wait loop, which is the standard idiom of using wait => easily googlable.Paz
S
3

Though this is not an ideal scenario for using multi-threading but as this is assignment I am putting one solution that works. The threads will execute sequentially and there are few point to note:

  1. Current thread cannot move ahead to read the line in the file until and unless its immediately previous thread is done as they are supposed to read in round-robin fashion.
  2. After current thread is done reading the line it must notify the other thread else that thread will wait forever.

I have tested this code with some files in temp package and it was able to read the lines in round robin fashion. I believe Phaser can also be used to solve this problem.

    public class FileReaderRoundRobinNew {
        public Object[] locks;

        private static class LinePrinterJob implements Runnable {
            private final Object currentLock;


private final Object nextLock;
        BufferedReader bufferedReader = null;

        public LinePrinterJob(String fileToRead, Object currentLock, Object nextLock) {
            this.currentLock = currentLock;
            this.nextLock = nextLock;
            try {
                this.bufferedReader = new BufferedReader(new FileReader(fileToRead));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            /*
             * Few points to be noted:
             * 1. Current thread cannot move ahead to read the line in the file until and unless its immediately previous thread is done as they are supposed to read in round-robin fashion.
             * 2. After current thread is done reading the line it must notify the other thread else that thread will wait forever.
             * */
            String currentLine;
            synchronized(currentLock) {
                try {
                    while ( (currentLine = bufferedReader.readLine()) != null) {
                        try {
                            currentLock.wait();
                            System.out.println(currentLine);
                        }
                        catch(InterruptedException e) {}
                        synchronized(nextLock) {
                            nextLock.notify();
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            synchronized(nextLock) {
                nextLock.notify(); /// Ensures all threads exit at the end
            }
        }
    }

    public FileReaderRoundRobinNew(int numberOfFilesToRead) {
        locks = new Object[numberOfFilesToRead];
        int i;
        String fileLocation = "src/temp/";
        //Initialize lock instances in array.
        for(i = 0; i < numberOfFilesToRead; ++i) locks[i] = new Object();
        //Create threads
        int j;
        for(j=0; j<(numberOfFilesToRead-1); j++ ){
            Thread linePrinterThread = new Thread(new LinePrinterJob(fileLocation + "Temp" + j,locks[j],locks[j+1]));
            linePrinterThread.start();
        }
        Thread lastLinePrinterThread = new Thread(new LinePrinterJob(fileLocation + "Temp" + j,locks[numberOfFilesToRead-1],locks[0]));
        lastLinePrinterThread.start();
    }

    public void startPrinting() {
        synchronized (locks[0]) {
            locks[0].notify();
        }
    }

    public static void main(String[] args) {
        FileReaderRoundRobinNew fileReaderRoundRobin = new FileReaderRoundRobinNew(4);
        fileReaderRoundRobin.startPrinting();
    }
}

If the only objective is to read the files in round-robin fashion and not strictly in same order then we can also use Phaser. In this case the order in which files are read is not always same, for example if we have four files (F1, F2, F3 and F4) then in first phase it can read them as F1-F2-F3-F4 but in next one it can read them as F2-F1-F4-F3. I am still putting this solution for sake of completion.

public class FileReaderRoundRobinUsingPhaser {

    final List<Runnable> tasks = new ArrayList<>();
    final int numberOfLinesToRead;

    private static class LinePrinterJob implements Runnable {
        private BufferedReader bufferedReader;

        public LinePrinterJob(BufferedReader bufferedReader) {
            this.bufferedReader = bufferedReader;
        }

        @Override
        public void run() {
            String currentLine;
            try {
                currentLine = bufferedReader.readLine();
                System.out.println(currentLine);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public FileReaderRoundRobinUsingPhaser(int numberOfFilesToRead, int numberOfLinesToRead) {
        this.numberOfLinesToRead = numberOfLinesToRead;
        String fileLocation = "src/temp/";
        for(int j=0; j<(numberOfFilesToRead-1); j++ ){
            try {
                tasks.add(new LinePrinterJob(new BufferedReader(new FileReader(fileLocation + "Temp" + j))));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    public void startPrinting( ) {
        final Phaser phaser = new Phaser(1){
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("Phase Number: " + phase +" Registeres parties: " + getRegisteredParties() + " Arrived: " + getArrivedParties());
                return ( phase >= numberOfLinesToRead || registeredParties == 0);
            }
        };

        for(Runnable task : tasks) {
            phaser.register();
            new Thread(() -> {
                do {
                    phaser.arriveAndAwaitAdvance();
                    task.run();
                } while(!phaser.isTerminated());
            }).start();
        }
        phaser.arriveAndDeregister();
    }

    public static void main(String[] args) {
        FileReaderRoundRobinUsingPhaser fileReaderRoundRobin = new FileReaderRoundRobinUsingPhaser(4, 4);
        fileReaderRoundRobin.startPrinting();
        // Files will be accessed in round robin fashion but not exactly in same order always. For example it can read 4 files as 1234 then 1342 or 1243 etc.
    }

}

The above example can be modified as per exact requirement. Here the constructor of FileReaderRoundRobinUsingPhaser takes the number of files and number of lines to read from each file. Also the boundary conditions need to be taken into consideration.

Sacken answered 29/4, 2015 at 5:13 Comment(0)
P
2

You are missing many parts of the puzzle:

  • you attempt to synchronize on an object local to each thread. This can have no effect and the JVM may even remove the whole locking operation;

  • you execute notifyAll without a matching wait;

  • the missing wait must be at the top of the run method, not at the bottom as you indicate.

Altogether, I'm afraid that fixing your code at this point is beyond the scope of one StackOverflow answer. My suggestion is to first familiarize yourself with the core concepts: the semantics of locks in Java, how they interoperate with wait and notify, and the precise semantics of those methods. An Oracle tutorial on the subject would be a nice start.

Paz answered 19/8, 2013 at 13:41 Comment(2)
I changed the object on which i need to synchronized. I took a class, and synchronized on its instance. But, still did not get how can i continue the execution in round robin.. I am able to do mutual exclusion and read one file completely by one thread, then start next threadEskew
You can use a static variable which will hold the number of the file which should be read next. You should then test this value in each thread for equality with the number you pass in as the constructor argument. This must happen in a wait loop, which is the standard idiom of using wait => easily googlable.Paz

© 2022 - 2024 — McMap. All rights reserved.