Hi,
I hope this will give you some idea.
import javax.swing.*;
import java.awt.event.*;
class Status extends JFrame
{
private String title = "Status: idle";
private String contents = "Nothing happening";
public Status()
{
JButton pop_it_up = new JButton( "Show status" );
pop_it_up.addActionListener
( new ActionListener()
{ public void actionPerformed( ActionEvent e )
{ JOptionPane.showMessageDialog( null,
contents, title, JOptionPane.INFORMATION_MESSAGE );
}
}
);
getContentPane().add( pop_it_up );
pack();
show();
}
public synchronized void change_status( String status, String explanation )
{ this.title = "Status: " + status;
this.contents = explanation;
}
public static void main( String[] args )
{
Status myself = new Status();
myself.change_status( "Busy", "I'm busy");
work();
myself.change_status( "Done", "I'm done");
work();
myself.change_status( "Busy", "I'm busy again");
work();
myself.change_status( "Done", "I'm done again");
work();
System.exit( 0 );
}
private static void work()
{ try{ Thread.currentThread().sleep(4000); }catch( Exception e ){}
}
}
So, what's the problem? Everything's synchronized, isn't it? Well, not really. The problem is that, even though the word Thread appears nowhere in Listing 1, this is nonetheless a multithreaded app:
there's the main thread (on which main() executes) and there's the AWT/Swing thread that handles GUI events like button presses (as discussed last month). Both threads are running in parallel and
are accessing the same Status object. Now imagine the following sequence of events:
1.main() executes, popping up the main frame and setting the status message.
2.main() finishes the first piece of work, and sends the message
myself.change_status( "Done", "I'm done doing something");
to the main-frame object. This method is synchronized, so it appears safe.
3.Half-way through the execution of change_status() (after the title is changed, but before the contents are changed) the user hits the "Show status" button. The main thread is preempted,
and the AWT thread wakes up to process the button press.
4.To see what happens next, examine the event-handler setup code on line 13. An anonymous inner-class object handles the button press, and it manufactures the message dialog using the
title and contents fields. At this point, however, the title field has been modified, but the contents have not, so the displayed title doesn't jibe with the actual message.
A first (incorrect) attempt to solve the problem might be to synchronize the actionPerformed() method:
pop_it_up.addActionListener
( new ActionListener()
{ public synchronized void actionPerformed( ActionEvent e )
{ JOptionPane.showMessageDialog( null,
contents, title, JOptionPane.INFORMATION_MESSAGE );
}
}
);
This doesn't work, though. Remember, we have two objects and two monitors. Locking the inner-class object does not affect access to the outer-class object, which contains the two fields that are
giving us grief. The only solution is to synchronize on the object that actually contains the fields that the two threads are accessing -- the outer-class object:
pop_it_up.addActionListener
( new ActionListener()
{ public void actionPerformed( ActionEvent e )
{
synchronized( Status.this )
{
JOptionPane.showMessageDialog( null,
contents, title, JOptionPane.INFORMATION_MESSAGE );
}
}
}
);
To be safe, all inner-class listeners that access outer-class fields should synchronize on the outer class object in this way.
Notifier-side problems: notifications in a multithreaded world
Flipping the perspective over to that of the notifier, various thread-related problems emerge here, too:
1.Observers can be added and removed from the current list while notifications are in progress.
2.Events can occur so fast that several notifications go on at the same time, perhaps using different, but overlapping, observer lists.
3.Observers are notified even though they have been removed from the list of observers.
Let's start by analyzing the modification-while-notifications-are-in-progress problem, which in some ways is the hardest to solve. AWT listeners can be added or removed at any time, even when
notifications are in progress. In fact, a listener can even remove itself from a list of listeners as it services the notification message passed to it. The following code is perfectly legal:
Button some_button;
//...
some_button.addActionListener
( new ActionListener()
{ public void actionPerformed( ActionEvent e )
{ some_button.removeActionListener( this );
//...
}
}
);
I hope this will help you.
Thanks
Bakrudeen