multithreaded oo (contd.) questionspammann/332/ppt/kak/slides18d.pdfconsider the case of a bank...
TRANSCRIPT
Multithreaded OO (contd.)
Questions:
1. In Java, how does one eliminate thread caused
by the different threads trying to modify a
shared object?
1
2. How many threads are allowed to simultaneously
execute the code that is in a synchronized method?
2
3. Given a thread that is executing a synchronized
instance method of an object. What does the thread
do to the entire object?
3
4. How many threads may have a lock on an object
at any one time if the object contains a
synchronized method?
4
5. What’s the difference between a thread that
is executing a synchronized instance method
of an object and a thread that is executing
a synchronized static method of a class?
5
7. When a thread wishing to make a certain
modification to a data object finds out that
the state of the data object does not meet the
required conditions, the thread must execute
what method?
7
8. When a thread creates a certain data condition
that is needed by other threads, what must the
condition-creating thread do?
8
10. If a thread got placed in a monitor
object’s waiting list because it executed the
wait() method, what’s the only way for it come
off the waiting list?
10
Java’s wait–notify Mechanism for
Dealing with Deadlock
Let’s consider again the situation of different threads modifying a common
data object and let’s assume that different threads produce different types
of modifications.
11
Consider the case of a bank account that is shared by multiple customers.
Some customers may deposit money into the account while other cus-
tomers may withdraw money from the account.
If a customer finds that the balance in the account is less than the amount
he/she wanted to withdraw, the customer would want to wait until one or
more depositors added to the account.
12
If we represent each customer by a thread, then a thread wanting to
withdraw money would want to wait until the threads depositing monies
into the account had produced a sufficient balance.
In multithreaded programming, this effect can be achieved by using the
wait–notify mechanism.
13
The important thing to remember is that if a thread landed in a wait list
because it executed wait(), the only way it can come off the list is if it
receives a notification sent by notifyAll() in some other thread.
Instead of notifyAll(), it is also possible to use notify(), which sends
the notification to a randomly selected thread in the wait list.
14
//filename: MultiCustomerAccount.java
/////////////// class Account ////////////////
class Account {
int balance;
Account() {
balance = 0;
}
synchronized void deposit( int dep ) {
balance += dep;
notifyAll();
}
synchronized void withdraw( int draw ){
while ( balance < draw ) {
try {
wait();
} catch( InterruptedException e ) {}
}
balance -= draw;
}
}
15
//////////////// class Depositor //////////////
class Depositor extends Thread {
private Account acct;
Depositor( Account act ){
acct = act;
}
public void run() {
int i = 0;
while ( true ) {
int x = (int) ( 10 * Math.random() );
acct.deposit( x );
if ( i++ % 1000 == 0 )
System.out.println( "balance after deposits: "
+ acct.balance );
try { sleep( 5 ); } catch( InterruptedException e ) {}
}
}
}
16
////////////// class Withdrawer //////////////
class Withdrawer extends Thread {
private Account acct;
Withdrawer( Account act ) {
acct = act;
}
public void run() {
int i = 0;
while ( true ) {
int x = (int) ( 10 * Math.random() );
acct.withdraw( x );
if ( i++ % 1000 == 0 )
System.out.println( "balance after withdrawals: "
+ acct.balance );
try { sleep( 5 ); } catch( InterruptedException e ) {}
}
}
}
17
//////////// class MultiCustomerAccount //////////
class MultiCustomerAccount {
public static void main( String[] args ) {
Account account = new Account();
Depositor[] depositors = new Depositor[ 5 ];
Withdrawer[] withdrawers = new Withdrawer[ 5 ];
for ( int i=0; i < 5; i++ ) {
depositors[ i ] = new Depositor( account );
withdrawers[ i ] = new Withdrawer( account );
depositors[ i ].start();
withdrawers[ i ].start();
}
}
}
18
A typical output of this program is
balance after deposits: 12
balance after deposits: 18
balance after deposits: 26
balance after deposits: 18
balance after deposits: 35
balance after withdrawals: 27
balance after withdrawals: 20
balance after withdrawals: 20
balance after withdrawals: 10
balance after withdrawals: 10
balance after deposits: 119
balance after deposits: 127
balance after deposits: 127
balance after deposits: 122
balance after deposits: 114
balance after withdrawals: 109
balance after withdrawals: 110
balance after withdrawals: 110
balance after withdrawals: 106
balance after withdrawals: 103
.....
.....
19
Data I/O Between Threads in Java
Java provides special input and output streams for setting up data transfer
links between threads.
Streams of bytes can be output by a thread via the PipedOutputStream
and input into a thread via the PipedInputStream.
20
import java.io.*;
////////////// class TopLevel /////////////////
public class TopLevel {
public static void main( String[] args ) {
try {
PipedOutputStream pout = new PipedOutputStream();
PipedInputStream pin = new PipedInputStream( pout );
// that’s how the pipes get connected
// throws IOException
Module_1 mod1 = new Module_1( pout );
Module_2 mod2 = new Module_2( pin );
mod1.start();
mod2.start();
} catch( IOException e ){}
}
}
21
////////////// class Module_1 ///////////////
class Module_1 extends Thread {
private DataOutputStream out;
public Module_1( OutputStream outsm ) {
out = new DataOutputStream( outsm );
}
public void run() {
for (;;) {
try {
double num = Math.random();
out.writeDouble( num );
System.out.println( "Number written into the pipe: " + num );
out.flush();
sleep( 500 );
} catch( Exception e ) { System.out.println( "Error: " + e ); }
}
}
}
22
////////////// class Module_2 ///////////////
class Module_2 extends Thread {
private DataInputStream in;
public Module_2( InputStream istr ) {
in = new DataInputStream( istr );
}
public void run() {
for (;;) {
try {
double x = in.readDouble();
System.out.println( " Number received from the pipe: " + x );
}
catch( IOException e ){
System.out.println( "Error: " + e );
}
}
}
}
23
<HTML>
<TITLE>
Animation Applet
</TITLE>
<BODY BGCOLOR="#000000">
<APPLET CODE="Animator.java" WIDTH=480 HEIGHT=590>
<PARAM name=imagename value="bird">
<PARAM name=imagecount value="4">
</APPLET>
</BODY>
</HTML>
25
//html filename: Animator.html
/*
<HTML>
<TITLE>
Animation Applet
</TITLE>
<BODY BGCOLOR="#000000">
<APPLET CODE="Animator.java" WIDTH=480 HEIGHT=590>
<PARAM name=imagename value="bird">
<PARAM name=imagecount value="4">
</APPLET>
</BODY>
</HTML>
*/
26
//Animator.java
import java.applet.*;
import java.awt.*;
public class Animator extends Applet implements Runnable {
private String imageNameBase;
private int imageCount;
private Thread runner;
private Image image = null;
private Image[] imageArr;
public void init() {
imageNameBase = getParameter( "imagename" );
imageCount = Integer.parseInt(
getParameter( "imagecount" ) );
imageArr = new Image[ imageCount ];
int i = 0;
while ( i < imageCount ) {
String imageName = imageNameBase + i + ".gif";
imageArr[i] =
getImage( getDocumentBase(), imageName );
MediaTracker tracker = new MediaTracker( this );
tracker.addImage( imageArr[i], 0 );
try {
tracker.waitForID( 0 );
} catch( InterruptedException e ) {}
i++;
}
}
public void start() {
runner = new Thread( this );
runner.start();
}
27
public void stop() {
runner = null;
}
public void paint( Graphics g ) {
if ( image == null ) return;
g.drawImage( image, 100, 100, this );
}
public void run() {
int i = 0;
while ( true ) {
image = imageArr[i];
i = ++i % imageCount;
try {
Thread.sleep( 200 );
} catch( InterruptedException e ){}
repaint();
}
}
}
28
The Event Dispatch Thread for GUI
Programming with Java AWT/Swing
The event processing loop, executed in a separate thread in Java AWT/Swing,
is called the Event Dispatch Thread.
Sometimes it is also referred to as the AWT thread.
Ideally, all of the interactions of a user with a GUI, such as when the user
clicks on a button, or when the user chooses a menu item, etc., should be
handled by this thread.
29
//EventThreadDemo.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
class EventThreadDemo {
public static void main( String[] args ) {
JFrame frame = new JFrame( "Event Thread Demo" );
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
System.exit( 0 );
}
});
JTextArea textArea = new JTextArea();
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.getDocument().addDocumentListener(new MyDocumentListener());
JScrollPane areaScrollPane = new JScrollPane(textArea);
areaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
areaScrollPane.setPreferredSize(new Dimension(250, 250));
areaScrollPane.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Plain Text"),
BorderFactory.createEmptyBorder(5,5,5,5)),
areaScrollPane.getBorder()));
frame.getContentPane().add( areaScrollPane, BorderLayout.CENTER );
frame.pack();
30
frame.setVisible( true );
keepBusy( 500, "main" );
}
static class MyDocumentListener implements DocumentListener {
public void insertUpdate( final DocumentEvent e ) {
String str = null;
Document doc = e.getDocument();
int lengthText = doc.getLength();
try {
str = doc.getText( lengthText - 1, 1 );
} catch( BadLocationException badloc ) {
badloc.printStackTrace();
}
keepBusy( 500, "MyDocumentListener" );
System.out.print( str );
}
public void removeUpdate(DocumentEvent e) { }
public void changedUpdate(DocumentEvent e) { }
}
31
public static void keepBusy( int howLong, String source ) {
if (SwingUtilities.isEventDispatchThread() == true )
System.out.println(
" using Event Dispatch Thread for keepBusy in " + source );
else
System.out.println(
" using the main thread for keepBusy in " + source );
long curr = System.currentTimeMillis();
while ( System.currentTimeMillis() < curr + howLong )
;
}
}
32
using the main thread for keepBusy in main
h using Event Dispatch Thread for keepBusy in MyDocumentListener
e using Event Dispatch Thread for keepBusy in MyDocumentListener
l using Event Dispatch Thread for keepBusy in MyDocumentListener
l using Event Dispatch Thread for keepBusy in MyDocumentListener
o using Event Dispatch Thread for keepBusy in MyDocumentListener
33
Ordinarily, this would also be the case if we constructed a more complex
GUI with multiple top-level components and multiple listeners.
All of the code in all the listener methods would get executed in a single
thread, the Event Dispatch Thread.
The advantages of this are obvious – you do not run into synchronization
issues, in the sense that you do not have to worry that the displayed text
in a text area may appear as partially old and partially new.
34
//EventThreadDemo2.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
class EventThreadDemo {
public static void main( String[] args ) {
LaunchAFrame laf1 = new LaunchAFrame();
LaunchAFrame laf2 = new LaunchAFrame();
laf1.start();
laf2.start();
}
}
class LaunchAFrame extends Thread {
public LaunchAFrame() {}
public void run() {
MyTools.printThreadInfo( "Just before creating Frame object:" );
JFrame frame = new JFrame( "EventThreadsDemo 2" );
MyTools.printThreadInfo( "Just after creating Frame object:" );
frame.addWindowListener( new WindowAdapter() {
public void windowClosing( WindowEvent e ) {
System.exit( 0 );
}
});
JTextArea textArea = new JTextArea();
textArea.setLineWrap(true);
35
textArea.setWrapStyleWord(true);
textArea.getDocument().addDocumentListener(new MyDocumentListener());
MyTools.printThreadInfo( "Just after registering document listener:" );
JScrollPane areaScrollPane = new JScrollPane(textArea);
MyTools.printThreadInfo( "Just after creating the scroll pane:" );
areaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
areaScrollPane.setPreferredSize(new Dimension(250, 250));
areaScrollPane.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Plain Text"),
BorderFactory.createEmptyBorder(5,5,5,5)),
areaScrollPane.getBorder()));
frame.getContentPane().add( areaScrollPane, BorderLayout.CENTER );
MyTools.printThreadInfo( "Just before calling pack:" );
frame.pack();
frame.setLocation( 300, 300 );
frame.setVisible( true );
MyTools.printThreadInfo( "Just after calling setVisible:" );
}
}
36
class MyTools {
public static void printThreadInfo( String s ) {
System.out.println( s );
// Thread.currentThread().getThreadGroup().list();
// System.out.println( "Number of threads in the current thread group: "
// + Thread.currentThread().getThreadGroup().activeCount() );
System.out.println( "The current thread is: "
+ Thread.currentThread() );
}
public static void keepBusy( int howLong ) {
// if (SwingUtilities.isEventDispatchThread() == true )
// System.out.println( "Using Event Dispatch Thread for keepBusy" );
long curr = System.currentTimeMillis();
while ( System.currentTimeMillis() < curr + howLong )
;
}
}
37
class MyDocumentListener implements DocumentListener {
public void insertUpdate( final DocumentEvent e ) {
String str = null;
Document doc = e.getDocument();
int lengthText = doc.getLength();
try {
str = doc.getText( lengthText - 1, 1 );
} catch( BadLocationException badloc ) {
badloc.printStackTrace();
}
MyTools.printThreadInfo( "From iniside the listener: " );
MyTools.keepBusy( 500 );
System.out.print( str );
}
public void removeUpdate(DocumentEvent e) { }
public void changedUpdate(DocumentEvent e) { }
}
38
Just before creating Frame object:
The current thread is: Thread[Thread-0,5,main]
Just before creating Frame object:
The current thread is: Thread[Thread-1,5,main]
Just after creating Frame object:
The current thread is: Thread[Thread-1,5,main]
Just after creating Frame object:
The current thread is: Thread[Thread-0,5,main]
Just after registering document listener:
The current thread is: Thread[Thread-0,5,main]
Just after registering document listener:
The current thread is: Thread[Thread-1,5,main]
Just after creating the scroll pane:
The current thread is: Thread[Thread-1,5,main]
Just after creating the scroll pane:
The current thread is: Thread[Thread-0,5,main]
Just before calling pack:
The current thread is: Thread[Thread-0,5,main]
Just before calling pack:
The current thread is: Thread[Thread-1,5,main]
39
Just after calling setVisible:
The current thread is: Thread[Thread-0,5,main]
Just after calling setVisible:
The current thread is: Thread[Thread-1,5,main]
40
From iniside the listener:
The current thread is: Thread[AWT-EventQueue-0,6,main]
h
From iniside the listener:
The current thread is: Thread[AWT-EventQueue-0,6,main]
e
....
....
From iniside the listener:
The current thread is: Thread[AWT-EventQueue-0,6,main]
j
From iniside the listener:
The current thread is: Thread[AWT-EventQueue-0,6,main]
e
....
....
41
Once a Swing component is realized, all code that might affect or
depend on the state of that component should be executed in the Event
Dispatch Thread.
Realized means that a component has been painted on-screen or that it is
ready to be painted.
A Swing component that’s a top-level window is realized by having one of
these methods invoked on it: setVisible(true), show() or pack().
Once a window is realized, all the components that it contains are realized.
Another way to realize a component is to add it to a container that’s
already realized.
42
The upside of all of the listener code being processed by the Event Dispatch
Thread:
.....one event handler will finish executing before the next one is taken up
The downside:
....is that the code in each event handler must execute quickly so as not
to degrade the GUI’s overall response to user interaction.
43
Basically, the GUI remains frozen to user interaction during the execution
of an event handler code.
This makes it necessary for the code in an event handler to execute very
quickly and that if more extensive processing (of, say, the information
elicited from the user) is required, a separate thread be spawned.
44
public static void invokeLater( java.lang.Runnable doRun )
public static void invokeAndWait( java.lang.Runnable doRun )
throws InterruptedException, InvocationTargetException
45
Invocation of invokeLater causes doRun.run() to be executed asyn-
chronously in the Event Dispatch Thread.
Asynchronously means that invokeLater returns immediately after its
invocation, and that the actual execution of doRun.run() takes place
after all pending AWT/Swing events have been processed.
46
Runnable doHelloWorld = new Runnable() {
public void run() {
System.out.println("Hello World on "
+ Thread.currentThread());
}
};
SwingUtilities.invokeLater(doHelloWorld);
System.out.println("This might well be displayed "
+ "before the other message.");
47
InvokeAndWait causes doRun.run() to be executed synchronously on
the Event Dispatch Thread.
Synchronously here means that the call to InvokeAndWait will block
until all pending AWT/Swing events have been processed and (then)
doRun.run() returns.
It should not be called from the Event Dispatch Thread.
48
final Runnable doHelloWorld = new Runnable() {
public void run() {
System.out.println("Hello World on " +
Thread.currentThread());
}
};
Thread appThread = new Thread() {
public void run() {
try {
SwingUtilities.invokeAndWait(doHelloWorld);
}
catch (Exception e) {
e.printStackTrace();
}
System.out.println("Finished on "
+ Thread.currentThread());
}
};
appThread.start();
49
class MyDocumentListener implements DocumentListener {
public void insertUpdate( final DocumentEvent e ) {
new Thread() {
public void run() {
String str = null;
Document doc = e.getDocument();
int lengthText = doc.getLength();
try {
str = doc.getText( lengthText - 1, 1 );
} catch( BadLocationException badloc ) {
badloc.printStackTrace();
}
MyTools.printThreadInfo( "From iniside the listener: " );
MyTools.keepBusy( 500 );
System.out.print( str );
}
}.start();
}
public void removeUpdate(DocumentEvent e) { }
public void changedUpdate(DocumentEvent e) { }
}
50
For more complex situations, when the separate thread launched in the
event handler is suppose to alter the state of the GUI after finishing the
time-consuming operation, it is best to use the SwingWorker class from
the Sun site.
This class optionally executes additional code in the Event Dispatch Thread
for altering the state of the GUI at the conclusion of the time-consuming
operation.
51
import javax.swing.SwingUtilities;
public abstract class SwingWorker {
private Object value; // see getValue(), setValue()
private Thread thread;
/**
* Class to maintain reference to current worker thread
* under separate synchronization control.
*/
private static class ThreadVar {
private Thread thread;
ThreadVar(Thread t) { thread = t; }
synchronized Thread get() { return thread; }
synchronized void clear() { thread = null; }
}
private ThreadVar threadVar;
/**
* Get the value produced by the worker thread, or null if it
* hasn’t been constructed yet.
*/
protected synchronized Object getValue() {
return value;
}
/**
* Set the value produced by worker thread
*/
private synchronized void setValue(Object x) {
value = x;
}
/**
* Compute the value to be returned by the <code>get</code> method.
52
*/
public abstract Object construct();
/**
* Called on the event dispatching thread (not on the worker thread)
* after the <code>construct</code> method has returned.
*/
public void finished() {
}
/**
* A new method that interrupts the worker thread. Call this method
* to force the worker to stop what it’s doing.
*/
public void interrupt() {
Thread t = threadVar.get();
if (t != null) {
t.interrupt();
}
threadVar.clear();
}
/**
* Return the value created by the <code>construct</code> method.
* Returns null if either the constructing thread or the current
* thread was interrupted before a value was produced.
*
* @return the value created by the <code>construct</code> method
*/
public Object get() {
while (true) {
Thread t = threadVar.get();
if (t == null) {
return getValue();
}
try {
53
t.join();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // propagate
return null;
}
}
}
public SwingWorker() {
final Runnable doFinished = new Runnable() {
public void run() { finished(); }
};
Runnable doConstruct = new Runnable() {
public void run() {
try {
setValue(construct());
}
finally {
threadVar.clear();
}
SwingUtilities.invokeLater(doFinished);
}
};
Thread t = new Thread(doConstruct);
threadVar = new ThreadVar(t);
}
/**
* Start the worker thread.
*/
public void start() {
54
The time-consuming task in the event handler is placed in the override
definition of the construct() method of the SwingWorker class:
final SwingWorker worker = new SwingWorker() {
public Object construct() {
//...time-consuming task in the event handler
return someValue;
}
};
worker.start();
...
56