Reputation: 21
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
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
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
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