Don't Understand wait() and notify()

J

Jason Cavett

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.
 
T

Tom Hawtin

Jason said:
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.

The object that is used as a lock isn't really relevant. However, it's a
good idea to keep as much as possible private. That includes locks. So,
IMO it's generally best to use a dedicated lock object. As a little
trick you can use a nested (or local) class with the name Lock, to help
in stack dumps (more later). So:

private static class Lock { }
private final Object lock = new 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.

The way to start to diagnose these problems is with a dump of all the
thread stacks. Type ctrl-\ (or ctrl-break in Windows) from the console,
use the jstack tool in recent JDKs or use a debugger.

If you want a blocking Queue, there is a ready made, high performance
solution in the form of java.util.concurrent.BlockingQueue and its
implementations.
public synchronized void notifyWorker() {
this.notify();
}

This is an obvious problem. You should set some state within the
synchronized block and then notify (or actually better is the other way
around).

Also you want to consider could this ever be used in a situation where
there is more than one thread waiting. If the answer is yes, or not
really sure, use notifyAll.
public synchronized void waiting() {
try {
this.wait();
this.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

Again, the notify should only happen after some state change, and wait
should be in a while loop.
// work forever
while (true) {
// check for work
File file = processor.dequeueFile();

if (file == null) {
synchronized(processor) {
processor.waiting();
}
} else {
// do stuff here
}
}

There appears to be a race condition here. What happens if between the
dequeueFile and the synchronized, a file is queued?

Generally loops like this should look, for a non-blocking queue
implementation, something like:

for (;;) {
final File file;
synchronized (lock) {
while (queue.isEmpty()) {
lock.wait();
}
file = queue.poll();
}
... do stuff with file ...
}
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.

You should be in the Event Dispatch Thread to update any controls. Use
java.awt.EventQueue.invokeLater.

Tom Hawtin
 
B

blmblm

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).

Nitpick: Maybe it would be clearer to call this a FileWorker, so it's
clear that's not actually a type of thread, but just something to hold
the run() method.
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

My first thought would be use a blocking queue. More in another
post, since it seems that someone else has also replied suggesting
that.
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();
}


Probable "gotcha": I think you mean start() rather than run() --
run() executes the right code, but in the wrong thread --
in the thread calling the FileProcessor constructor rather
than in the thread you just created.
 
B

blmblm

The object that is used as a lock isn't really relevant. However, it's a
good idea to keep as much as possible private. That includes locks. So,
IMO it's generally best to use a dedicated lock object. As a little
trick you can use a nested (or local) class with the name Lock, to help
in stack dumps (more later). So:

private static class Lock { }
private final Object lock = new Lock();


The way to start to diagnose these problems is with a dump of all the
thread stacks. Type ctrl-\ (or ctrl-break in Windows) from the console,
use the jstack tool in recent JDKs or use a debugger.

If you want a blocking Queue, there is a ready made, high performance
solution in the form of java.util.concurrent.BlockingQueue and its
implementations.

This might also simplify the whole design. If you look at the API
documentation (javadocs) for this class, it gives a very simple
producer/consumer example that I think is similar to what the OP
wants to do. All the wait() and notify() calls are buried in the
library code. Using a blocking queue would have been my first
thought about how to approach this problem, and how nice that there's
now a library implementation -- several in fact.

[ snip lots of excellent advice that the OP should review if he'd
rather stick closer to his initial plan ]
 
S

SadRed

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();
}
}
}
 
P

Patricia Shanahan

SadRed wrote:
....
(Local file system access can't be but sequential. So, multi-
threading on
them is futile, though.)
....

Why must local file system access be sequential?

Patricia
 
S

SadRed

SadRed wrote:

...> (Local file system access can't be but sequential. So, multi-

...

Why must local file system access be sequential?

Patricia

Oh sorry. I didn't know that you use a multi-head hard disk device!
 
P

Patricia Shanahan

SadRed said:
Oh sorry. I didn't know that you use a multi-head hard disk device!

You still seem to be implying that local file system access is limited
to one device. I've worked with systems that had dozens of disk drives.

However, even if there is only one disk head, getting multiple I/O's
issued can enable disk head movement optimization. If the I/Os are
issued one at a time, the drive has to do them in that order, which is
not necessarily an efficient order.

Patricia
 
S

SadRed

You still seem to be implying that local file system access is limited
to one device. I've worked with systems that had dozens of disk drives.

However, even if there is only one disk head, getting multiple I/O's
issued can enable disk head movement optimization. If the I/Os are
issued one at a time, the drive has to do them in that order, which is
not necessarily an efficient order.

Patricia
I've worked with systems that had dozens of disk drives.
And they had supported parallel I/Os. How gorgeous!
multiple I/O's
issued can enable disk head movement optimization
The most optimized disk head movement is NOT hopping
around multiple sectors spread over the platter. Anyway,
I believe there's no such thing as disk head movement
optimization that could support a pseudo-parallel, sip
by sip reads/writes. And even if there were, oveall
performance would be almost same as pure serial access.
Disk device used to be an awkward machinery, after all.

As a theoretical hypotheses, we exclude the use of
memory mapped file I/O for this particular forum thread.
 
G

Gordon Beaton

This is an obvious problem. You should set some state within the
synchronized block and then notify (or actually better is the other
way around).

I'm curious - why is it better to notify *before* setting the state?

Intuitively, setting before notifying is the correct order, even
though the synchronization effectively removes the importance of order
here.

/gordon

--
 
P

Patricia Shanahan

SadRed said:
And they had supported parallel I/Os. How gorgeous!

I was one of the developers of the Sun E10000. With up to 64 processors,
we needed far higher I/O bandwidth and transaction rate than we could
have got out of a single I/O path. I don't think we ever even thought
about sequential local file system access as a design option.

Patricia
 
S

SadRed

I was one of the developers of the Sun E10000. With up to 64 processors,
we needed far higher I/O bandwidth and transaction rate than we could
have got out of a single I/O path. I don't think we ever even thought
about sequential local file system access as a design option.

Patricia

Wow 64 pocessors! Good. You are an elite and we are not. We are not
talking about your level of system in every topic of public fora. You
even don't understand that but you should.
 
T

Tom Hawtin

Gordon said:
I'm curious - why is it better to notify *before* setting the state?

Intuitively, setting before notifying is the correct order, even
though the synchronization effectively removes the importance of order
here.

Usually it's not particularly important, and it's very rare to see it done.

The usual way to think about synchronized block execution is that one
statement is executed after another until the end of block is reached.
However there are other ways of leaving the block. return, break and
continue of course. Hopefully you should spot those.

The other way to leave a synchronized block is through an exception. If
the state has been modified such that another thread should wakeup, you
will want to have notified. Theoretically even the call to notify()
could cause an exception.

More often than not, the state update is simple and any theoretical risk
can be, relatively safely, swept under the carpet.

Tom Hawtin
 
C

Christian

SadRed said:
Wow 64 pocessors! Good. You are an elite and we are not. We are not
talking about your level of system in every topic of public fora. You
even don't understand that but you should.
and even in a normal system I would say it is not the job of the
programmer to handle that disc access is parallel or seriell...

at least not if its only a few parallel accesses.

Its the job of the OS to perform IO and properly cache disc reads... and
the disc's cache should also be helping...

If you really want to help your os .. well use a large ByteBuffer..


though the example stays .. there are things that must be accessed
sequential.. use wait/notify there..
 
L

Lew

SadRed said:
Wow 64 pocessors! Good. You are an elite and we are not. We are not
talking about your level of system in every topic of public fora. You
even don't understand that but you should.

Attacking the speaker does not invalidate the claim.

-- Lew
 
J

Jason Cavett

Nitpick: Maybe it would be clearer to call this a FileWorker, so it's
clear that's not actually a type of thread, but just something to hold
the run() method.



My first thought would be use a blocking queue. More in another
post, since it seems that someone else has also replied suggesting
that.












Probable "gotcha": I think you mean start() rather than run() --
run() executes the right code, but in the wrong thread --
in the thread calling the FileProcessor constructor rather
than in the thread you just created.



















--
B. L. Massingill
ObDisclaimer: I don't speak for my employers; they return the favor.- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -- Hide quoted text -

- Show quoted text -
Probable "gotcha": I think you mean start() rather than run() --
run() executes the right code, but in the wrong thread --
in the thread calling the FileProcessor constructor rather
than in the thread you just created.

Whoops. My mistake. I saw that after I posted to the code here.
Thanks for the reminder, though.
 
P

Patricia Shanahan

SadRed said:
Wow 64 pocessors! Good. You are an elite and we are not. We are not
talking about your level of system in every topic of public fora. You
even don't understand that but you should.

I'm afraid you are right that I don't understand the idea of separating
computing into "elite" and "non-elite" categories, with only the
"non-elite" considered fit for discussion in public fora.

Out of order execution was one of the special features of the CDC 6600,
presumably "elite". These days, it is normal in desktop processors.

RAID was originally a way of applying to servers the price/capacity of
mass produced disk drives, but the last time I saw a terabyte scale
RAID-5 disk array it was on the external disk drive shelf at a local
home electronics store.

A few years ago someone who divided computing into "elite" and
"non-elite" might have classed parallelizing compute bound work as an
"elite" technique, because, of course, no computer one should talk about
in public fora could possibly really run more than one thread at a time.

My working assumption is that there are three possible states for an
"elite" computer performance idea: it is already on desktops, or it is
coming soon to a desktop near you, or it has been superseded by better
ideas.

Patricia
 
J

Jason Cavett

The object that is used as a lock isn't really relevant. However, it's a
good idea to keep as much as possible private. That includes locks. So,
IMO it's generally best to use a dedicated lock object. As a little
trick you can use a nested (or local) class with the name Lock, to help
in stack dumps (more later). So:

private static class Lock { }
private final Object lock = new Lock();


The way to start to diagnose these problems is with a dump of all the
thread stacks. Type ctrl-\ (or ctrl-break in Windows) from the console,
use the jstack tool in recent JDKs or use a debugger.

If you want a blocking Queue, there is a ready made, high performance
solution in the form of java.util.concurrent.BlockingQueue and its
implementations.


This is an obvious problem. You should set some state within the
synchronized block and then notify (or actually better is the other way
around).

Also you want to consider could this ever be used in a situation where
there is more than one thread waiting. If the answer is yes, or not
really sure, use notifyAll.


Again, the notify should only happen after some state change, and wait
should be in a while loop.



There appears to be a race condition here. What happens if between the
dequeueFile and the synchronized, a file is queued?

Generally loops like this should look, for a non-blocking queue
implementation, something like:

for (;;) {
final File file;
synchronized (lock) {
while (queue.isEmpty()) {
lock.wait();
}
file = queue.poll();
}
... do stuff with file ...
}


You should be in the Event Dispatch Thread to update any controls. Use
java.awt.EventQueue.invokeLater.

Tom Hawtin

The BlockingQueue works very well, thank you. Of course, as always, I
run into other problems (now I can't copy the ProjectModel because
BlockingQueue's aren't serializable, doh) but at least you've sent me
down a correct path.

I'll look up invokeLater. I figured there was some way to sync the
thread and the GUI. My issue revolves around how I "hook" the GUI in
with the thread that's running. More of a design issue (which would
be too complicated for me to address here) than anything.

Thanks for your help. It is much appreciated.
 
J

John W. Kennedy

SadRed said:
And they had supported parallel I/Os. How gorgeous!

Real computers have been able to do that since the early 60s.
The most optimized disk head movement is NOT hopping
around multiple sectors spread over the platter. Anyway,
I believe there's no such thing as disk head movement
optimization that could support a pseudo-parallel, sip
by sip reads/writes. And even if there were, oveall
performance would be almost same as pure serial access.

So much for nearly half a century of computer science and
operating-system design.

There's more to the world than bog-standard personal computers, you
know, and there's more to performance optimization than your offhand
guesses.
 
B

blmblm

Usually it's not particularly important, and it's very rare to see it done.

The usual way to think about synchronized block execution is that one
statement is executed after another until the end of block is reached.
However there are other ways of leaving the block. return, break and
continue of course. Hopefully you should spot those.

The other way to leave a synchronized block is through an exception. If
the state has been modified such that another thread should wakeup, you
will want to have notified. Theoretically even the call to notify()
could cause an exception.

More often than not, the state update is simple and any theoretical risk
can be, relatively safely, swept under the carpet.

Seems to me that maybe the important thing is whether you want to
always perform the notify(), even if there are exceptions or errors,
or sometimes perform it and sometimes not, and that putting it first
would be appropriate for the first case but not for the second. Or
am I missing the point you're making?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top