multithreaded oo (contd.) questionspammann/332/ppt/kak/slides18d.pdfconsider the case of a bank...

56
Multithreaded OO (contd.) Questions: 1. In Java, how does one eliminate thread caused by the different threads trying to modify a shared object? 1

Upload: ngokhuong

Post on 01-Apr-2019

212 views

Category:

Documents


0 download

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

6. How exactly is thread synchronization achieved?

6

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

9. When a thread executes notifyAll(), who

receives the notification?

9

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

Java Threads for Applets

bird0.gif

bird1.gif

bird2.gif

bird3.gif

24

<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

Thread t = threadVar.get();

if (t != null) {

t.start();

}

}

}

55

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