Prashant Kamble
Prashant Kamble

Reputation: 21

How to do wait and notify with two independent threads?

I have created two independent threads using ExecutorService. Now I just want that one thread will write data into a file and another thread will read it after getting notification from the thread which is writing data into a file, but the output is not showing anything, so how can I achieve my goal.

My code is:
package threadingexamples;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadingExamples {

public static void main(String[] args) throws InterruptedException {
    ExecutorService es = Executors.newFixedThreadPool(2);
    es.submit(new ForLoo1());
    es.submit(new ForLoop2());

    es.shutdown();
    es.awaitTermination(1, TimeUnit.DAYS);

    System.exit(0);
}

}

class ForLoo1 implements Callable<Object> {

@Override
public Object call() throws Exception {

    System.out.println("I am writing content into file....");

    String s = "This is the content to write into a file";

    File file = new File("/home/f.txt");

    if (!file.exists()) {
        file.createNewFile();
    }

    FileWriter fw = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(s);
    bw.close();
    System.out.println("Now you can read content from files...");
    notify();
    return null;
}

}

class ForLoop2 implements Callable<Object> {

    @Override
    public Object call() throws Exception {

        wait();
        System.out.println("Okay i am now going to read content of   files...");

        BufferedReader br = new BufferedReader(new FileReader("f.txt"));
        String str;
        while ((str = br.readLine()) != null) {
            str = str + "";
        }
        System.out.println("I am done with reading.....");
        System.out.println(str);
        return null;
    }

}

Upvotes: 1

Views: 343

Answers (3)

Nathan Hughes
Nathan Hughes

Reputation: 96454

You can do this by using a Future. Submitting the first task returns a Future that the second task can use to retrieve the first task's return value once that task completes; the Callable task receiving the future will block until the get method receives a result. I changed the first callable to return a non-null value:

package threadingexamples;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Future;

public class ThreadingExamples {

    public static void main(String[] args) throws InterruptedException {

        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Object> future = es.submit(new ForLoo1());
        es.submit(new ForLoop2(future));

        es.shutdown();
        es.awaitTermination(1, TimeUnit.DAYS);
    }

}

class ForLoo1 implements Callable<Object> {

    @Override
    public Object call() throws Exception {

        System.out.println("I am writing content into file....");

        String s = "This is the content to write into a file";

        File file = new File("/home/f.txt");

        if (!file.exists()) {
            file.createNewFile();
        }

        FileWriter fw = new FileWriter(file);
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write(s);
        bw.close();
        System.out.println("Now you can read content from files...");
        return "ok";
    }
}

class ForLoop2 implements Callable<Object> {

    private Future<Object> future;

    public ForLoop2(Future<Object> future) {
        this.future = future;
    }

    @Override
    public Object call() throws Exception {
        System.out.println("in ForLoop2, ");
        Object ok = future.get();

        System.out.println("Okay i am now going to read content of   files...");

        BufferedReader br = new BufferedReader(new FileReader("f.txt"));
        String str;
        while ((str = br.readLine()) != null) {
            str = str + "";
        }
        System.out.println("I am done with reading.....");
        System.out.println(str);
        return null;
    }
}

Alternatively you could use a BlockingQueue, that you pass into each Callable's constructor. One Callable places an entry in the queue and the other reads from it. That's more suitable for the situation where you expect multiple messages to be passed between threads. It is the same principle, though: put the wait/notify code in the data structure, not in the task.

Upvotes: 0

zpc
zpc

Reputation: 107

You can achieve the intended effect with the code below. By calling await in ForLoop2, the thread will only wake up after the countDown is called in ForLoop1. CountDownLatch is a versatile synchronization tool.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadingExamples {

public static void main(String[] args) throws InterruptedException {
    final CountDownLatch cdl = new CountDownLatch(1);
    ExecutorService es = Executors.newFixedThreadPool(2);
    es.submit(new ForLoo1(cdl));
    es.submit(new ForLoop2(cdl));
    es.shutdown();
    es.awaitTermination(1, TimeUnit.DAYS);

}

}

class ForLoo1 implements Callable<Object> {
    CountDownLatch cdl;
    public ForLoo1(CountDownLatch cdl){
        this.cdl=cdl;
    }

@Override
public Object call() throws Exception {

    System.out.println("I am writing content into file....");

    String s = "This is the content to write into a file";

    File file = new File("/home/f.txt");

    if (!file.exists()) {
        file.createNewFile();
    }

    FileWriter fw = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(s);
    bw.close();
    System.out.println("Now you can read content from files...");
    cdl.countDown();
    return null;
}

}

class ForLoop2 implements Callable<Object> {
    CountDownLatch cdl;
    public ForLoop2(CountDownLatch cdl){
        this.cdl=cdl;
    }

    @Override
    public Object call() throws Exception {

        cdl.await();
        System.out.println("Okay i am now going to read content of   files...");

        BufferedReader br = new BufferedReader(new FileReader(new File("/home/f.txt")));
        String str;
        System.out.println("I am done with reading.....");
        while ((str = br.readLine()) != null) {
            System.out.println(str);
        }

        return null;
    }

}

Upvotes: 2

Larry B.
Larry B.

Reputation: 753

EDIT: This is an example of what not to do. Please use the CountDownLatch method; you should not need to use notify or wait in most programs. Also, read the comments below for exactly why this is a bad idea. Even with the added synchronized blocks (which are needed to call notify and wait), there's still a race condition that could lead to deadlock in ForLoop2.


You've almost got it! When you call wait() and notify() in the code above, they're being called in different objects. Try this:

Object monitorObject = new Object();
es.submit(new ForLoo1(monitorObject));
es.submit(new ForLoop2(monitorObject));

...


class ForLoop1 implements Callable<Object> {
    private final Object monitorObject;

    public ForLoop1(Object monitorObject) {
         this.monitorObject = monitorObject;
    }

    @Override
    public Object call() throws Exception {

        ...
        synchronized(monitorObject) {
            monitorObject.notify();
        }
    }
}

class ForLoop2 implements Callable<Object> {
    private final Object monitorObject;

    public ForLoop2(Object monitorObject) {
         this.monitorObject = monitorObject;
    }

    @Override
    public Object call() throws Exception {

        synchronized(monitorObject) {
            monitorObject.wait();
        }
        ...
    }
}

Upvotes: -1

Related Questions