|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 7
Using a condition variable to wait for events
The following code illustrates a classic problem that is easily solved with a condition variable: How do I wait for an event
to occur without wasting machine cycles in a polling loop? The code sets up a simple TextField and an ActionListener that's notified when the user types the Enter key:
01 | class Some_class extends Frame
02 | {
03 | TextField input = new TextField();
04 | String entered = "";
05 |
06 | public The_wrong_way()
07 | { input.addActionListener
08 | ( new ActionListener()
09 | { public void actionPerformed( ActionEvent e )
10 | { entered = input.getText();
11 | }
12 | }
13 | );
14 |
15 | add(input);
16 | pack();
17 | show();
18 | }
19 |
20 | String read_line(){ return entered; }
21 | //...
22 | }
So far, so good, but let's look at the situation in more detail.
When you display the Frame, AWT fires up a thread that monitors events coming in from the operating system, including key-press events. When the Enter
key is pressed, for example, the AWT thread gets the key-press event from the OS and, in turn, calls the listener's actionPerformed() method. The "actionPerformed()" messages are coming in asynchronously from the AWT event-loop thread. Put another way: the
"actionPerformed()" message is actually running on that AWT thread.
Meanwhile, a user thread (as differentiated from the AWT thread) calls read_line() to find out what the user has typed. The problem is that both the AWT and the user thread can access the entered field simultaneously -- a classic race condition. The second thread could call read_line() while the AWT thread is in the middle of ActionPerformed() and end up reading garbage.
Solve this first problem with synchronization:
01 | class Some_class extends Frame
02 | {
03 | TextField input = new TextField();
04 | String entered = "";
05 |
06 | public The_wrong_way()
07 | { input.addActionListener
08 | ( new ActionListener()
09 | { public void actionPerformed( ActionEvent e )
10 | { synchronized( Some_class.this )
11 | { entered = input.getText();
12 | }
13 | }
14 | }
15 | );
16 |
17 | add(input);
18 | pack();
19 | show();
20 | }
21 |
22 | String synchronized read_line(){ return entered; }
23 | //...
24 | }
Note that the inner-class method has to synchronize explicitly on the outer-class object. Simply synchronizing actionPerformed() doesn't work because you'll be synchronizing on the monitor of the anonymous inner-class object, and the field you want to
guard is in the outer-class object.
Moving on, our user thread needs to know when an entire line has been typed to be sure that read_line() will return a complete line of input, but (and this is the big but), there's no direct communication between the two threads involved in this transaction. The code running on the AWT thread
(actionPerformed()) doesn't tell the user thread that an entire-line-has-been-typed event has occurred.
So how does the caller of read_line() know the string has changed? It could sit in a tight polling loop calling read_line() and checking the current return value against the previously returned value, but that's an awful lot of machine cycles wasted
on doing nothing useful.