Re: Don't Understand wait() and notify()
On Mar 29, 7:30 am, "Jason Cavett" <jason.cav...@gmail.com> wrote:
I will be the first to admit I don't *really* understand threads.
Have a general idea, but, as I haven't had to use them too often, I
never really got a chance to learn them. Well...I'm working with them
now and that leads me to my question.
I am attempting to create an application that queues up files (given
by the user via the GUI) that will be processed. Whenever a file is
added to the queue, the processor takes the file off the queue and
processes it. The user should be able to continue working in the GUI
normally (as they would expect).
So far, I have three components to this aspect of my application.
They are:
ProjectModel - This is a user's project. It holds a list of the files
they're working on. This also provides the model that the GUI (View)
is created from. Anyway, what's important is that the ProjectModel
holds a "FileProcessor" object.
FileProcessor - The FileProcessor is simply a class that has a Vector
of Files. It provides (synchronized) methods to enqueue and dequeue
files. The FileProcessor has a FileWorkerThread object. The
FileWorkerThread is started in the FileProcessor's constructor.
FileWorkerThread - The FileWorkerThread object has a run() method
(implements Runnable) and does the work of processing the file.
FileWorkerThread also has a reference to the FileProcessor object (the
same exact object as above - it's passed in on the FileWorkerThread's
constructor).
The issue I am having, is I don't understand how wait and notify apply
in this case. I was initially attempting to use the FileProcessor
object as a lock. When the FileWorkerThread started up, if nothing
was in the queue of the FileProcessor, the FileWorkerThread was
supposed to wait until there was something in the queue.
Unfortunately, when I called wait() on the FileProcessor object (the
one that I was synchronizing over) the GUI would hang.
What I am attempting to have is something like this:
Open up a Project -> Queue a file in FileProcessor -> Notify the
FileWorkerThread that there are items waiting for it in the queue ->
FWT does the work -> When no more items are in the queue, FWT waits
until it receives another notify
Here is the code for the FileProcessor and the FileWorkerThread:
---
public class FileProcessor {
private Vector<File> files;
private FileWorkerThread worker;
public FileProcessor() {
files = new Vector<File>();
// worker thread setup
worker = new FileWorkerThread(this);
Thread workerThread = new Thread(worker, "Worker Thread");
workerThread.run();
}
public synchronized void enqueueFile(File file) {
files.add(file);
}
public synchronized File dequeueFile() {
File file = null;
if (files.size() != 0) {
file = files.firstElement();
files.remove(file);
}
return file;
}
public synchronized void removeFile(File file) {
files.remove(file);
}
public synchronized void removeFile(int position) {
files.remove(position);
}
public synchronized void notifyWorker() {
this.notify();
}
public synchronized void waiting() {
try {
this.wait();
this.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}}
---
public class FileWorkerThread implements Runnable {
private FileProcessor processor;
public FileWorkerThread(FileProcessor processor) {
this.processor = processor;
}
/**
* @see java.lang.Runnable#run()
*/
public void run() {
// work forever
while (true) {
// check for work
File file = processor.dequeueFile();
if (file == null) {
synchronized(processor) {
processor.waiting();
}
} else {
// do stuff here
}
}
}
}
Thanks
P.S. At some point, I'm going to want to use the worker thread to
update a progress bar - I don't know if that'll affect anything or if
there's anything additional I will need to think about.
/*
If you use java.util.concurrent.BlockingQueue object for enqueueing/
dequeueing of Files, or application's other principal resources, wait/
notify is automatically handled in the object. You don't need to worry
about anything for them.
As a programming exercise, however, you should do wait/notify *on
the methods of data structure object itself*, <b>not on its user
objects</b>. Your current code violates this most important principle.
I hope you try and study this code.
(Local file system access can't be but sequential. So, multi-
threading on
them is futile, though.)
*/
import java.io.*;
import java.util.*;
public class FileProcessorDriver{
public static void main(String[] args){
File[] files = new File(".").listFiles(new FilenameFilter(){
public boolean accept(File dir, String name){
return (name.endsWith(".txt")); // text file only
}
});
FileProcessorX fp = new FileProcessorX(files);
FileWorker fw1 = new FileWorker(fp, "FW1");
FileWorker fw2 = new FileWorker(fp, "FW2");
FileWorker fw3 = new FileWorker(fp, "FW3");
fw1.start();
fw2.start();
fw3.start();
}
}
class FileProcessorX{
static final int CAPACITY = 100;
Vector<File> files;
private int fnum;
public FileProcessorX(File[] fs) {
files = new Vector<File>(Arrays.asList(fs));
fnum = files.size();
}
public synchronized void enqueueFile(File file) {
while (fnum >= CAPACITY){ // saturated
try{
wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
}
++fnum;
files.add(file);
notifyAll(); // wake up ones who were waiting on dequeueFile
}
public synchronized File dequeueFile() {
while (files.isEmpty()){
try{
wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
}
--fnum;
notifyAll(); // wake up ones who were waiting on enqueueFile
return files.remove(0);
}
}
class FileWorker extends Thread{
FileProcessorX fpx;
Vector<File> fvec;
String mark;
public FileWorker(FileProcessorX fp, String mk){
fpx = fp;
mark = mk;
}
public void run(){
while (true){
File f = fpx.dequeueFile();
work(f);
}
}
void work(File file){
String line = null;
try{
BufferedReader br = new BufferedReader(new FileReader(file));
while ((line = br.readLine()) != null){
System.out.println(mark + " : " + line);
}
}
catch (Exception e){
e.printStackTrace();
}
}
}