Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
5 views80 pages

Java End Term

It's the end term (week 9 - week 12) theory of the IITM BS degrees Java course. Quiz 1, quiz 2, and the end term sum up the whole Java course offered by IITM BS degree.

Uploaded by

veredem846
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views80 pages

Java End Term

It's the end term (week 9 - week 12) theory of the IITM BS degrees Java course. Quiz 1, quiz 2, and the end term sum up the whole Java course offered by IITM BS degree.

Uploaded by

veredem846
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 80

Optional Types
Type 📒 Lecture
Date @February 21, 2022

Lecture
1
#

Lecture
https://youtu.be/3Q7Rp6sxE_M
URL

Notion https://21f1003586.notion.site/Optional-Types-
URL 45386c4ede174512955ea513e3c21c70

Week # 9

Dealing with empty streams


Largest and smallest values seen

max() and min()

Requires a comparison function

What happens if the stream is empty?

Optional<Double> maxrand =
Stream.generate(Math::random)

Optional Types 1
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

max() of empty stream is undefined

Return value could be Double or null

Optional<T> object

Wrapper

May contain an object of type T

Value is present

Or no object

Handling missing optional values


Or orElse() to pass a default value (if the desired value is not present)

Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

Double fixrand = maxrand.orElse(-1.0);

Use orElseGet() to call a function which generates replacement for a missing


value

Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

Double fixrand = maxrand.orElseGet(


() -> SomeFunctionToGenerateDouble
);

Use orElseThrow() to generate an exception when a missing value is


encountered

Optional Types 2
Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

Double fixrand = maxrand.orElseThrow(


IllegalStateException::new
);

Ignoring missing values


Use ifPresent() to test if a value is present, and process it

Missing value is ignored

optionalValue.ifPresent(v -> Process v);

For instance, add maxrand to a collection results , if it is present

Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

var results = new ArrayList<Double>();


maxrand.ifPresent(v -> results.add(v));

Or, we can pass the function in different forms as well

Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

var results = new ArrayList<Double>();


maxrand.ifPresent(results::add);

Specify an alternative action if the value is not present

Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)

Optional Types 3
.filter(n -> n < 0.001)
.max(Double::compareTo);

var results = new ArrayList<Double>();

maxrand.isPresentOrElse(
v -> results.add(v),
() -> System.out.println("No max")
);

Creating an optional value


Create an optional value

Optional.of(v) creates value v

Optional.empty creates empty optional

public static Optional<Double> inverse(Double x) {


if(x == 0) {
return Optional.empty();
} else {
return Optional.of(1 / x);
}
}

Use ofNullable() to transform null automatically into an empty optional

Useful when working with functions that return object of type T or null ,
rather than Optional<T>

Example, the above code will produce a result if x is not 0 otherwise, it will
return empty

public static Optional<Double> inverse(Double x) {


return Optional.ofNullable(1 / x);
}

Passing on optional values


Can produce an output Optional value from an input Optional

map applies function to value, if present

If input is empty, so is output

Optional Types 4
Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

Optional<Double> maxrandasqr =
maxrand.map(v -> v * v);

Another example

Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

var results = new ArrayList<Double>();

maxrand.map(results::add);

Supply an alternative for a missing value

If value is present, it is passed as is

If value is empty, value generated by or() is passed

Optional<Double> maxrand =
Stream.generate(Math::random)
.limit(100)
.filter(n -> n < 0.001)
.max(Double::compareTo);

Optional<Double> fixrand =
maxrand.or(() -> Optional.of(-1.0));

Composing optional values of different types


Suppose that

f() returns Optional<T>

Class T defines g() , returning Optional<U>

Cannot compose s.f().g()

s.f() has type Optional<T> , not T

Optional Types 5
Instead, use flatMap

s.f().flatMap(T::g)

If s.f() is present, apply g()

Otherwise return empty Optional<U>

Optional<U> result = s.f().flatMap(T::g);

For example, pass output of earlier safe inverse() to safe SquareRoot()

public static Optional<Double> inverse(Double x) {


if(x == 0) {
return Optional.empty();
} else {
return Optional.of(1/x);
}
}

public static Optional<Double> squareRoot(Double x) {


if(x < 0) {
return Optional.empty();
} else {
return Optional.of(Math.sqrt(x));
}
}

Optional<Double> result = inverse(x).flatMap(MyClass::squareRoot);

Turning an optional into a stream


Suppose lookup(u) returns a User if u is a valid username

Optional<User> lookup(String id) { ... }

We want to convert a stream of userids into a stream of users

Input is Stream<String>

Output is Stream<User>

But lookup returns Optional<User>

Pass through a flatMap

Optional Types 6
Stream<String> ids = ...;
Stream<User> users =
ids.map(Users::lookup)
.flatMap(Optional::stream);

What if lookup was implemented without using Optional ?

oldLookup returns User or null

Use ofNullable to regenerate Optional<User>

Stream<String> ids = ...;


Stream<User> users = ids.flatMap(
id -> Stream.ofNullable(
Users.oldLookup(id)
)
);

Summary
Optional<T> is a clean way to encapsulate a value that may be absent

Different ways to process values of type Optional<T>

Replace the missing value by a default

Ignore missing values

Can create values of type Optional<T> where outcome may be undefined

Can write functions that transform optional values to optional values

flatMap allows us to cascade functions with optional types

Use flatMap to regenerate a stream from optional values

Optional Types 7

Collecting results from Streams
Type 📒 Lecture
Date @February 21, 2022

Lecture # 2

Lecture https://youtu.be/v0fNOGui3Wg
URL

Notion https://21f1003586.notion.site/Collecting-results-from-Streams-
URL 65c4b17d4cf44d7b83c48c486776e3a0

Week
9
#

Collecting values from a stream


Convert collections into sequences of values — streams

Process a stream as a collection?

Stream definees a standard iterator, use to loop through values in a stream

Alternatively, use forEach with a suitable function

Can convert a stream into an array usint toArray()

Collecting results from Streams 1


Creates an array of Object by default

Pass array constructor to get a more specific array type

mystream.forEach(System.out::println);

Object[] result = mystream.toArray();

String[] result = mystream.toArray(String[]::new);


// mystream.toArray() has the type Object[]

Storing a stream as a collection


What if we want to convert the stream back into a collection?

Use collect()

Pass appropriate factory method from Collectors

Static method that directly calls a constructor

Implicitly creates an object

Create a list from a stream

List<String> result = mystream.collect(Collectors.toList());

... or a set

Set<String> result = mystream.collect(Collectors.toSet());

To create a concrete collection, provide a constructor

TreeSet<String> result = stream.collect(


Collectors.toCollection(TreeSet::new)
);

Stream summaries
We saw how to reduce a stream to a single result value — count(), max(), ...

In general, need a stream of numbers

Collectors has methods to aggregate summaries in a single object

Collecting results from Streams 2


summarizingInt works for a stream of integers

Pass function to convert given stream to numbers — here String::length

Returns IntSummaryStatistics that stores count, max, min, sum, average

IntSummaryStatistics summary =
mystream.collect(
Collectors.summarizingInt(String::length);
);

double averageWordLength = summary.getAverage();


double maxWordLength = summary.getMax();

Methods to access relevant statistics

getCount()

getMax()

getMin()

getSum()

getAverage()

Similarly, summarizingLong() and summarizingDouble() return LongSummaryStatistics

and DoubleSummaryStatistics

Converting a stream to a map


Convert a stream of Person to a map

For Person p , p.getID() is the key and p.getName() is value

Stream<Person> people = ...;


Map<Integer, String> idToName =
people.collect(
Collectors.toMap(
Person::getId,
Person::getName
)
);

To store entire object as value, use Function.identity()

Stream<Person> people = ...;


Map<Integer, Person> idToPerson =

Collecting results from Streams 3


people.collect(
Collectors.toMap(
Person::getId,
Function.identity()
)
);

What happens if we use name for key and id for value?

Likely to have duplicate keys — IllegalStateException

Stream<Person> people = ...;


Map<String, Integer> nameToID =
people.collect(
Collectors.toMap(
Person::getName,
Person::getId
)
);

Provide a function to fix such problems

Stream<Person> people = ...;


Map<String, Integer> nameToID =
people.collect(
Collectors.toMap(
Person::getName,
Person::getId,
(existingValue, newValue) -> existingValue
)
);

Grouping and partitioning values


Instead of discarding values with duplicate keys, group them

Collect all ids with the same name in a list

Instead, may want to partition the stream using a predicate

Stream<Person> people = ...;


Map<String, List<Person>> nameToPersons =
people.collect(
Collectors.groupingBy(
Person::getName
)
);

Collecting results from Streams 4


Partition names into those that start with A and the rest

Key values of resulting map are true and false

Stream<Person> people = ...;


Map<String, List<Person>> aAndOtherPersons =
people.collect(
Collectors.partitioningBy(
p -> p.getName().substr(0,1).equals("A")
)
);

List<Person> startingLetterA = aAndOtherPersons.get(true);

Summary
We converted collections into sequences and processed them as streams

After transformations, we may want to process a stream as a collection

Use iterators, forEach() to process a stream element by element

Use toArray() to convert to an array

Factory methods in Collector allows us to convert a stream back into a


collection of our choice

Can convert an arbitrary stream into a stream of numbers and collect summary
statistics

Can convert a stream into a map

Can group values by a key, or partition by a predicate

Collecting results from Streams 5



Input/Output Streams
Type 📒 Lecture
Date @February 21, 2022

Lecture
3
#

Lecture
https://youtu.be/FFdDai4qf8w
URL

Notion https://21f1003586.notion.site/Input-Output-Streams-
URL c35b40f4d8ce4385abf3ec9b190a4e5e

Week # 9

Input and Output streams


Input → read a sequence of bytes from some source

A file, an internet connection, memory, ...

Output → write a sequence of bytes to some source

A file, an internet connection, memory, ...

Java refers to these as input and output streams

Not the same as stream objects in class Stream

Input/Output Streams 1
Input and Output values could be of different types

Ultimately, input and output are raw uninterpreted bytes of data

Interpret as text — different Unicode encodings

Or as binary data — integers, floats, doubles, ...

Use a pipeline of input/output stream transformers

Read raw bytes from a file, pass to a stream that reads text

Generate binary data, pass to a stream that writes raw bytes to a file

Reading and Writing raw bytes


Classes InputStream and OutputStream

Read one or more bytes — abstract methods are implemented by subclasses of


InputStream

Check availability before reading

abstract int read();


int read(byte[] b);
byte[] readAllBytes();
// ... and more

InputStream in = ....
int bytesAvailable = in.available();
if(bytesAvailable > 0) {
var data = new byte[bytesAvailable];
in.read(data);
}

Write bytes to output

Close a stream when done — release resources

Flush an output stream — output is buffered

abstract void write(int b);


void write(byte[] b);
// ... and more

OutputStream ot = ...
byte[] values = ...;
out.write(values);

in.close();

Input/Output Streams 2
out.flush();

Connecting a stream to an external source


Input and output streams ultimately connect to external resources

A file, an internet connection memory ...

We limit ourselves to files

Create an input stream attached to a file

Create an output stream attached to a file

Overwrite or append?

Pass a boolean second argument to the constructor

var in = new FileInputStream("input.class");

var out = new FileInputStream("output.bin");

// Overwrite
var out = new FileOutputStream("newoutput.bin", false);

// Append
var out = new FileOutputStream("sameoutput.bin", true);

Reading and Writing text


Recall Scanner class

Can apply to any input stream

Many read methods

var fin = new FileInputStream("input.txt");


var scin = new Scanner(fin);

String s = scin.nextLine(); // One line


String w = scin.next(); // One word
int i = scin.nextInt(); // Read an int
boolean b = scin.hasNext(); // Any more words?

To write text, use PrintWriter class

Apply to any output stream

Input/Output Streams 3
var fout = new FileOutputStream("output.txt");
var pout = new PrintWriter(fout);

Use println() , print() to write txt

String msg = "Hello, World!";


pout.println(msg);

Example: Copy input text file to output text file

var in = new Scanner(...);


var out = new PrintWriter(...);

while(in.hasNext()) {
String line = in.nextLine();
out.println(line);
}

Beware: input/output methods generate many different kinds of exceptions

Need to wrap code with try blocks

Reading and Writing binary data


To read binary data, use DataInputStream class

Can apply to any input stream

Many read methods

var fin = new FileInputStream("input.class");


var din = new DataInputStream(fin);

readInt, readShort, readLong


readFloat, readDouble
readChar, readUTF
readBoolean

To write binary data, use DataOutputStream class

Apply to any output stream

Many write methods

Input/Output Streams 4
var fout = new FileOutputStream("output.bin");
var dout = new DataOutputStream(fout);

writeInt, writeShort, writeLong


writeFloat, writeDouble
writeChar, writeUTF
writeBoolean
writeChars
writeByte

Example: Copy input binary file to output binary file

Catch exceptions

var in = new DataInputStream(...);


var out = new DataOutputStream(...);

int bytesAvailable = in.available();


while(bytesAvailable > 0) {
var data = new byte[bytesAvailable];
in.read(data);
out.write(data);
bytesAvailable = in.available();
}

Other features
Buffering an input stream

Reads blocks of data

More efficient

var din = new DataInputStream(


new BufferedInputStream(
new FileInputStream("grades.dat")
)
);

Speculative reads

Examine the first element

Return to stream if necessary

var pbin = new PushbackInputStream(


new BufferedInputStream(

Input/Output Streams 5
new FileInputStream("grades.dat")
)
);

int b = pbin.read();
if(b != '<') {
pbin.unread(b);
}

Streams are specialized

PushBackStream can only read() and unread()

Feed to a DataInputStream to read meaningful data

var pbin = new PushbackInputStream(


new BufferedInputStream(
new FileInputStream("grades.dat")
)
);

var din = new DataInputStream(pbin);

Java has a whole zoo of streams for different tasks

Random access files, zipped data, ...

Chain together streams in a pipeline

Read binary data from a zipped file


FileInputStream

ZipInputStream

DataInputStream

Summary
Java’s approach to input/output is to separate out concerns

Chain together different types of input/output streams

Connect an external source as input or output

Read and Write raw bytes

Interpret raw bytes as text

Interpret raw bytes as data

Buffering, speculative read, random access files, zipped data, ...

Input/Output Streams 6
Chaining together streams appears tedious, but adds flexibility

Input/Output Streams 7

Serialization
Type 📒 Lecture
Date @February 22, 2022

Lecture # 4

Lecture
https://youtu.be/oweQzNSHA5I
URL

Notion https://21f1003586.notion.site/Serialization-
URL ca1d3636d5584ebca8f284baa577fb12

Week # 9

Reading and Writing Objects


We can read and write binary data

DataInputStream , DataOutputStream

Read and write low level units

Bytes, integers, floats, characters, ...

Can we export and import objects directly?

Why would we want to do this?

Serialization 1
Backup objects onto disk, with state

Restore objects from the disk

Send objects across a network

Serialization and Deserialization

Reading and writing objects ...


To write objects, Java has another output stream type, ObjectOutputStream

var out = new ObjectOutputStream(


new FileOutputStream("employee.dat")
);

Use writeObject() to write out an object

var emp = new Employee(...);


var boss = new Manager(...);
out.writeObject(emp);
out.writeObject(boss);

To read back objects, use ObjectInputStream

var in = new ObjectInputStream(


new FileInputStream("employee.dat")
);

Retrieve objects in the same order they were written using readObject()

var e1 = (Employee) in.readObject();


var e2 = (Employee) in.readObject();

Class has to allow serialization — implement marker interface Serializable

public class Employee implements Serializable { ... }

How serialization works


ObjectOutputStream examines all the fields and saves their contents

Serialization 2
ObjectInputStream "reconstructs" the object, effectively calls a constructor

What happens when many objects share the same object as an instance
variable?

class Manager extends Employee {


private Employee secretary;
...
}

Two managers have the same secretary

Each object is assigned a serial number — hence, serialization

When first encountered, save the data to output stream

If saved previously, record the serial number

Reverse the process when reading

Customizing serialization
Some objects should not be serialized — value of file handles, ...

Mark such fields as transient

public class LabeledPoint implements Serializable {


private String label;
private transient Point2D.Double point;
...
}

Can override writeObject()

defaultWriteObject() writes out the object with all non-transient fields

Then explicitly write relevant details of transient fields

private void writeObject(ObjectOutputStream out)


throws IOException {
out.defaultWriteObject();
out.writeDouble(point.getX());
out.writeDouble(point.getY());
}

... and readObject()

Serialization 3
defaultReadObject() reconstructs object with all non-transient fields

Then explicitly reconstruct transient fields

private void readObject(ObjectInputStream in)


throws IOException {
in.defaultReadObject();
double x = in.readDouble();
double y = in.readDouble();
point = new Point2D.Double(x, y);
}

Handle with care


Serialization is a good option to share data within an application

Over time, older serialized objects may be incompatible with newer versions

Some mechanisms for version control, but still some pitfalls possible

Deserialization implicitly invokes a constructor

Running code from an external source

Always a security risk

Summary
Serialization allows us to export and import objects, with state

Backup objects onto disk, with state

Restore objects from disk

Send objects across a network

Use ObjectOutputStream and ObjectInputStream to write and read objects

Serial numbers are used to ensure only a single copy of each shared object is
archived

Mark fields that should not be serialized as transient

Customize writeObject() and readObject()

Serialization carries risks of ...

Version control of objects

Running unknown code

Serialization 4

Concurrency: Threads &
Processes
Type 📒 Lecture
Date @February 25, 2022

Lecture # 1

Lecture https://youtu.be/dR0_7AmJKKk
URL

Notion https://21f1003586.notion.site/Concurrency-Threads-Processes-
URL eefdd659b1954818ab380430bc83f624

Week
10
#

Concurrent Programming
Multiprocessing

Single processor executes several computations “in parallel”

Time-slicing to share access

Concurrency: Threads & Processes 1


Logically parallel actions within a single application

Clicking Stop terminates a download in a browser

User-interface is running in parallel with network access

Process

Private set of local variables

Time-slicing involves saving the state of one process and loading the
suspended state of another

Threads

Operated on same local variables

Communicate via “shared memory”

Context switches are easier

The word “process” and “thread” interchangeably

Shared variables
Browser example: download thread and user-interface thread run in parallel

Shared boolean variable terminate indicates whether download should be


interrupted

terminate is initially false

Clicking Stop sets it to true

Download thread checks the value of this variable periodically and aborts if it
is set to true

Watch out for race conditions

Shared variables must be updated consistently

Creating threads in Java


Have a class extend Thread

public class Parallel extends Thread {


private int id;

public Parallel(int i) { id = i; }
}

Concurrency: Threads & Processes 2


Define a function run() where execution can begin in parallel

public class Parallel extends Thread {


private int id;
public Parallel(int i) { id = i; }

public void run() {


for(int j = 0; j < 100; j++) {
System.out.println("id: " + id);

try {
// Sleep for 1000ms
sleep(1000);
} catch(InterruptedException e) { ... }
}
}
}

Invoking p[i].start() initiates p[i].run() in a separate thread

public class TestParallel {


public static void main(String[] args) {
Parallel[] p = new Parallel[5];

for(int i = 0; i < 5; i++) {


p[i] = new Parallel(i);
// Start p[i].run() in a concurrent thread
p[i].start();
}
}
}

Directly calling p[i].run() does execute in separate thread

sleep(t) suspends thread for t milliseconds

Static function — use Thread.sleep() if current class does not extend Thread

throws InterruptedException

Typical Output

My id is 0
My id is 3
My id is 2
My id is 1
My id is 4
My id is 0
My id is 2
My id is 3

Concurrency: Threads & Processes 3


My id is 4
My id is 1
My id is 0
My id is 3
My id is 1
My id is 2
My id is 4
My id is 0
...

Java threads ...


Cannot always extend Thread

Single inheritance

Instead, implement Runnable

public class Parallel implements Runnable {


// only the line above has changed
private int id;
public Parallel(int i) { ... }
public void run() { ... }
}

To use the Runnable class, explicitly create a Thread and start() it

public class TestParallel {


public static void main(String[] args) {
Parallel[] p = new Parallel[5];
Thread[] t = new Thread[5];

for(int i = 0; i < 5; i++) {


p[i] = new Parallel(i);
// Make a new thread t[i] from p[i]
t[i] = new Thread(p[i]);
// Start off p[i].run() using t[i].start(). Weird syntax but ok
t[i].start();
}
}
}

Summary
Common to have logically parallel actions with a single application

Download from one webpage while browsing another

Threads are lightweight processes with shared variables that can run in parallel

Concurrency: Threads & Processes 4


Use Thread class or Runnable interface to create parallel threads in Java

Concurrency: Threads & Processes 5



Race Conditions
Type 📒 Lecture
Date @February 25, 2022

Lecture
2
#

Lecture
https://youtu.be/a9PZhjCKm9k
URL

Notion https://21f1003586.notion.site/Race-Conditions-
URL 7c32206056624d5aa6950d18f2a1cfff

Week # 10

Maintain data consistency


double accounts[100] describes 100 bank account

Two functions that operate on accounts: transfer() and audit()

boolean transfer(double amount, int source, int target) {


if(accounts[source] < amount) { return false; }

accounts[source] -= amount;
accounts[target] += amount;
return true;

Race Conditions 1
}

double audit() {
// Total balance across all accounts
double balance = 0.00;
for(int i = 0; i < 100; i++) {
balance += accounts[i];
}
return balance;
}

What are the possibilities when we execute the following ...

audit() can report an overall total that is 500 more or less than the actual
assets

Depends on how actions of transfer are interleaved with actions of audit

Can even report an error if transfer happens automatically

Atomicity of updates
Two threads increment a shared variable n

Expect n to increase by 2

but, time-slicing may order execution as follows

Race Conditions 2
Race conditions and mutual exclusion
Race condition — concurrent update of shared variables, unpredictable outcome

Executing transfer() and audit() concurrently can cause audit() to report


more or less than the actual assets

Avoid this by insisting that transfer() and audit() do not interleave

Never simultaneously have current control point of one thread within transfer()

and another thread within audit()

Mutually exclusive access to critical regions of code

Summary
Concurrent update of a shared variable can lead to data inconsistency

Race condition

Control behaviour of threads to regulate concurrent updates

Critical sections — sections of code where shared variables are updated

Mutual exclusion — at most one thread at a time can be in a critical section

Race Conditions 3

Mutual Exclusion
Type 📒 Lecture
Date @February 25, 2022

Lecture
3
#

Lecture
https://youtu.be/i6FNvH3ULMU
URL

Notion https://21f1003586.notion.site/Mutual-Exclusion-
URL b5fa37b4a93d48f5a0256e0f19a4ed1f

Week # 10

Mutual Exclusion
Concurrent update of a shared variable can lead to data inconsistency

Race condition

Control behaviour of threads to regulate concurrent updates

Critical sections — sections of code where shared variables are updated

Mutual exclusion — at most one thread at a time can be in a critical section

Mutual Exclusion 1
Mutual exclusion for two processes
First attempt

Shared variable turn — no assumption about initial value, atomic update

Mutually exclusive access is granted

but one thread is locked out permanently if other thread shuts down

Starvation

Second attempt

Mutually exclusive access is granted

but if both threads try simultaneously, they block each other

Deadlock

Mutual Exclusion 2
Peterson’s Algorithm

Combines the previous two approaches

if both try simultaneously, turn decides who goes through

If only one is alive, request for that process is stuck at false and turn is
irrelevant

Beyond two processes


Generalizing Peterson’s solution to more than two processes is not trivial

For n process mutual exclusion other solutions exists

Lamport’s Bakery Algorithm

Each new process picks up a token (increments a counter) that is larger than
all waiting processes

Lowest token number gets served next

Still need to break ties — token counter is not atomic

Need specific clevel solutions for different situations

Need to argue correctness in each case

Instead, provide higher level support in programming language for


synchronization

Mutual Exclusion 3
Summary
We can construct protocols that guarantee mutual exclusion to critical sections

Starvation and Deadlock concerns

These protocols cleverly use regular variables

No assumptions about initial values, atomicity of updates

Difficult to generalize such protocols to arbitrary situations

Look to programming language for features that control synchronization

Mutual Exclusion 4

Test and Set
Type 📒 Lecture
Date @February 25, 2022

Lecture # 4

Lecture
https://youtu.be/MbFbJy3mkqI
URL

Notion https://21f1003586.notion.site/Test-and-Set-
URL ea3e4162ffa2430b9ca0089c717fb792

Week # 10

Test and set


The fundamental issue preventing consistent concurrent updates of shared
variables is test-and-set

To increment a counter, check its current value, then add 1

If more than one thread does this in parallel, updates may overlap and get lost

Need to combine test and set into an atomic, indivisible step

Cannot be guaranteed without adding this as a language primitive

Test and Set 1


Semaphores
Programming language support for mutual exclusion

Dijkstra’s semaphores

Integer variable with atomic test-and-set operation

A semaphore S supports two atomic operations

P(s) — from Dutch passeren, to pass

V(s) — from Dutch vrygeven to release

P(s) atomically executes the following

V(s) atomically executes the following

Using semaphores
Mutual exclusion using semaphores

Test and Set 2


Semaphores guarantee

Mutual exclusion

Freedom from starvation

Freedom from deadlock

Problems with semaphores


Too low level

No clear relationship between a semaphore and the critical region than it


protects

All threads must cooperate to correctly reset semaphore

Cannot enforce that each P(S) has a matching V(S)

Can even execute V(S) without having done P(S)

Summary
Test-and-set is at the heart of most race conditions

Test and Set 3


Need a high-level primitive for atomic test-and-set in the programming language

Semaphores provide one such solution

Solutions based on test-and-set are low level and prone to programming errors

Test and Set 4



Monitors
Type 📒 Lecture
Date @February 26, 2022

Lecture # 5

Lecture
https://youtu.be/sK1-Qkeh8mE
URL

Notion https://21f1003586.notion.site/Monitors-
URL 0213122e84ab445a817d2945e774066a

Week # 10

Atomic test-and-set
Test-and-set is at the heart of most race conditions

Need a high level primitive for atomic test-and-set in the programming language

Semaphores provide one such solution

Solutions based on test-and-set are low level and prone to programming errors

Monitors
Attach synchronization control to the data that is being protected

Monitors 1
Monitors — Per Brinch Hansen and CAR Hoare

Monitor is like a class in an OO language

Data definition — to which access is restricted across threads

Collections of functions operating on this data — all are implicitly mutually


exclusive

monitor bank_account {
double accounts[100];

boolean transfer(double amount, int source, int target) {


if(accounts[source] < amount) {
return false;
}

accounts[source] -= amount;
accounts[target] += amount;
return true;
}

double audit() {
// compute balance across all accounts
double balance = 0.00;
for(int i = 0; i < 100; i++) {
balance += accounts[i];
}

return balance;
}
}

Monitor guarantees mutual exclusion — if one function is active, any other


function will have to wait for it to finish

Monitors: external queue


Monitor ensures transfer and audit are mutually exclusive

If Thread 1 is executing transfer and Thread 2 invokes audit , it must wait

Implicit queue associated with each monitor

Contains all processes waiting for access

In practice, this may be just a set, not a queue

Making monitors more flexible


Our definition of monitors may be too restrictive

Monitors 2
transfer(500.00, i, j);

transfer(400.00, j, k);

This should always succeed if accounts[i] > 500

If these calls are reordered and accounts[i] < 400 initially, this will fail

A possible fix — let an account wait for pending inflows

boolean transfer(double amount, int source, int target) {


if(accounts[source] < amount) {
// wait for another transaction to transfer money
// into accounts[source]
}

accounts[source] -= amount;
accounts[target] += amount;
return true;
}

Monitors — wait()

All other processes are blocked out while this process waits

Need a mechanism for a thread to suspend itself and give up the monitor

A suspended process is waiting for monitor to change its state

Have a separate internal queue, as opposed to external queue where initially


blocked threads wait

Dual operation to notify and wake up suspended processes

Monitors — notify()

boolean transfer(double amount, int source, int target) {


if(accounts[source] < amount) { wait(); }

accounts[source] -= amount;
accounts[target] += amount;
notify();
return true;
}

What happens when a process executes notify() ?

Signal and exit — notifying process immediately exits the monitor

notify() must be the last instruction

Monitors 3
Signal and wait — notifying process swaps roles and goes into the internal
queue of the monitor

Signal and continue — notifying process keeps control till it completes then one
of the notified processes steps in

Monitors — wait() and notify()

Should check the wait() condition again on wake up

Change of state may not be sufficient to continue — e.g. not enough inflow
into the account to allow transfer

A thread can be again interleaved betwen notification and running

At wake-up, the state was fine, but it has changed again due to some other
concurrent action

wait() should be in a while , not in an if

boolean transfer(double amount, int source, int target) {


if(accounts[source] < amount) { wait(); }

accounts[source] -= amount;
accounts[target] += amount;
notify();
return true;
}

Condition variables
After transfer, notify() is only useful for threads waiting for target account of
transfer to change state

Makes sense to have more than one internal queue

monitor bank_account {
double accounts[100];
// one internal queue for each account
queue q[100];

boolean transfer(double amount, int source, int target) {


while(accounts[source] < amount) {
// wait in the queue associated with source
q[source].wait();
}

accounts[source] -= amount;
accounts[target] += amount;

Monitors 4
// notify the queue associated with target
q[target].notify();
return true;
}

// compute the balance across all accounts


double audit() { ... }
}

Monitor can have condition variables to describe internal queues

Summary
Concurrent programming with atomic test-and-set primitives is error prone

Monitors are like abstract datatypes for concurrent programming

Encapsulate data and methods to manipulate data

Methods are implicitly atomic, regulate concurrent access

Each object has an implicit external queue of processes waiting for execute
a method

wait() and notify() allow more flexible operation

Can have multiple internal queues controlled by condition variables

Monitors 5

Monitors in Java
Type 📒 Lecture
Date @March 6, 2022

Lecture
1
#

Lecture
https://youtu.be/Srz0mewFaEg
URL

Notion https://21f1003586.notion.site/Monitors-in-Java-
URL 5f68cc1c54cf4033ade01d2bc87bb9c2

Week # 11

Monitors
Monitor is like a class in an OO languages

Data definition — to which access is restricted across threads

Collections of functions operating on this data — all are implicitly mutually


exclusive

Monitor guarantees mutual exclusion — if one function is active, any other


function will have to wait for it to finish

Monitors in Java 1
Implicit queue associated with each monitor

Contains all processes waiting for access

monitor bank_account {
double accounts[100];

boolean transfer(double amount, int source, int target) {


if(accounts[source] < amount) {
return false;
}

accounts[source] -= amount;
accounts[target] += amount;
return true;
}

double audit() {
// Compute balance across all accounts
double balance = 0.00;
for(int i = 0; i < 100; ++i) {
balance += accounts[i];
}
return balance;
}
}

Condition variables
Thread suspends itself and waits for a state change — q[source].wait()

Separate internal queue vs external queue for initially blocked threads

monitor bank_account {
double accounts[100];
queue q[100]; // one internal queue for each account

boolean transfer(double amount, int source, int target) {


while(accounts[source] < amount) {
q[source].wait(); // wait in the queue associated with source
}

accounts[source] -= amount;
accounts[target] += amount;
q[target].notify(); // notify the queue assocaited with target
return true;
}

// compute the balance across all accounts


double audit() { ... }
}

Monitors in Java 2
Notify change — q[target].notify()

Signal and exit — notifying process immediately exits the monitor

Signal and wait — notifying process swaps roles with notified process

Signal and continue — notifying process keeps control till it completes and then
one of the notified processes steps in

Monitors in Java
Monitors incorporated within existing class definitions

public class bank_account {


double accounts[100];

public synchronized boolean transfer(double amount, int source, int target) {


while(accounts[source] < amount) { wait(); }
accounts[source] -= amount;
accounts[target] += amount;
notifyAll();
return true;
}

public synchronized double audit() {


double balance = 0.0;
for(int i = 0; i < 100; ++i) {
balance += accounts[i]
}
return balance;
}

public double current_balance(int i) {


return accounts[i]; // not synchronized
}
}

Function declared synchronized is to be executed atomically

Each object has a lock

To execute a synchronized method, thread must acquire lock

Thread gives up lock when the method exits

Only one thread can have the lock at any time

Wait for the lock in external queue

wait() and notify() to suspend and resume

Wait — single internal queue

Monitors in Java 3
Notify

notify() signals one (arbitrary) waiting process

notifyAll() signals all waiting processes

Java uses signal and continue

Object locks
Use object locks to synchronize arbitrary blocks of code

f() and g() can start in parallel

Only one of the threads can grab the lock for o

public class XYZ {


Object o = new Object();

public int f() {


..
synchronized(o) { ... }
}

public double g() {


..
synchronized(o) { ... }
}
}

Each object has its own internal queue

Object o = new Object();


public int f() {
..
synchronized(o) {
...
o.wait(); // wait in queue attached to "o"
...
}
}

public double g() {


..
synchronized(o) {
...
o.notifyAll(); // Wake up queue attached to "o"
...
}
}

Monitors in Java 4
Can convert methods from “externally” synchronized to “internally” synchronized

public double h() {


synchronized(this) { ... }
}

“Anonymous” wait() , notify() , notifyAll() abbreviate this.wait() ,


this.notify() , this.notifyAll()

Object locks ...


Actually, wait() can be interrupted by an InterruptedException

Should write

try {
wait();
} catch(InterruptedException e) {
...
}

Error to use wait() , notify() , notifyAll() outside synchronized method

IllegalMonitorStateException

Likewise, use o.wait() , o.notify() , o.notifyAll() only in block synchronized on


o

Reentrant locks

public class Bank {


private Lock bankLock = new ReentrantLock();
...
public void transfer(int from, int to, int amount) {
bankLock.lock();

try {
accounts[from] -= amount;
accounts[to] += amount;
} finally {
bankLock.unlock();
}
}
}

Separate ReentrantLock class

Monitors in Java 5
Similar to a semaphore

lock() is like P(S)

unlock() is like V(S)

Always unlock() in finally — avoid abort while holding lock

Why reentrant?

Thread holding lock can reacquire it

transfer() may call getBalance() that also locks bankLock

Hold count increases with lock() , decreases with unlock()

Lock is available if hold count is 0

Summary
Every object in Java implicitly has a lock

Methods tagged synchronized are executed atomically

Implicitly acquire and release the object’s look

Associated condition variable, single internal queue

wait(), notify(), notifyAll()

Can synchronize an arbitrary block of code using an object

synchronized(o) { ... }

o.wait(), o.notify(), o.notifyAll()

Reentrant locks work like semaphores

Monitors in Java 6

Thread in Java
Type 📒 Lecture
Date @March 6, 2022

Lecture
2
#

Lecture
https://youtu.be/htwvFDP5t5I
URL

Notion https://21f1003586.notion.site/Thread-in-Java-
URL ae49acbc0e1342c6923cb09d9dc00572

Week # 11

Creating threads in Java


Have a class extend Thread

Define a function run() where exeuction can begin in parallel

Invoking p[i].start() initiates p[i].run() in a separate thread

Directly calling p[i].run() does not execute in separate thread

sleep(t) suspends thread for t milliseconds

Static function — use Thread.sleep() if current class does not extend Thread

Thread in Java 1
Throws InterruptedException

public class Parallel extends Thread {


private int id;

public Parallel(int i) { id = i; }

public void run() {


for(int j = 0; j < 100; j++) {
System.out.println("My id is " + id);
try {
sleep(1000); // Sleep for 1000ms
} catch(InterruptedException e) {}
}
}
}

public class TestParallel {


public static void main(String[] args) {
Parallel[] p = new Parallel[5];
for(int i = 0; i < 5; i++) {
p[i] = new Parallel(i);
p[i].start(); // Start p[i].run() in concurrent thread
}
}
}

Thread in Java 2
Java threads
Cannot always extend Thread

Single inheritance

Instead, implement Runnable

To use Runnable class, explicitly create a Thread and start() it

public class Parallel implements Runnable {


private int id;

public Parallel(int i) { id = i; }

public void run() {


for(int j = 0; j < 100; j++) {
System.out.println("My id is " + id);

Thread in Java 3
try {
sleep(1000); // Sleep for 1000ms
} catch(InterruptedException e) {}
}
}
}

public class TestParallel {


public static void main(String[] args) {
Parallel[] p = new Parallel[5];
Thread[] t = new Thread[5];
for(int i = 0; i < 5; i++) {
p[i] = new Parallel(i);
t[i] = new Thread(p[i]);
// Make a thread t[i] from p[i]
// Start off p[i].run()
t[i].start();
}
}
}

Life cycle of a Java thread


A thread can be in six states — thread status via t.getState()

New: Created but not start() ed

Runnable: start() ed and ready to be scheduled

Need not be actually “running”

No guarantee made about how scheduling is done

Most Java implementations use time-slicing

Not available to run

Blocked — waiting for a lock, unblocked when lock is granted

Waiting — suspended by wait() , unblocked by notify() or notifyAll()

Timed wait — within sleep(...) , released when sleep timer expires

Dead: thread terminates

Interrupts
One thread can interrupt another using interrupt()

p[i].interrupt(); interrupts thread p[i]

Raises InterruptedException within wait() , sleep()

Thread in Java 4
No exception raised if thread is running

interrupt() sets a status flag

interrupted() checks the interrupt status and clears the flag

Detecting an interrupt while running or waiting

public void run() {


try {
j = 0;
while(!interrupted() && j < 100) {
System.out.println("My id is " + id);
sleep(1000);
j++;
}
} catch(InterruptedException e) {}
}

More about threads


Check a thead’s interrupt status

Use t.isInterrupted() to check status of t ’s interrupt flag

Does not clear flag

Can give up running status

yield() gives up active state to another thread

Static method in Thread

Normally, scheduling of threads is handled by OS —preemptive

Some mobile platforms use cooperative scheduling — thread loses control


only if it yields

Waiting for other threads

t.join() waits for t to terminate

Summary
To run in parallel, need to extend Thread or implement Runnable

When implementing Runnable , first create a Thread from Runnable object

t.start() invokes method run() in parallel

Threads can become inactive for different reasons

Thread in Java 5
Block waiting for a lock

Wait in internal queue for a condition to be notified

Wait for a sleep timer to elapse

Threads can be interrupted

Be careful to check both interrupted status and handle InterruptException

Can yield control, or wait for another thread to terminate

Thread in Java 6

Concurrency Programming
Type 📒 Lecture
Date @March 6, 2022

Lecture
3
#

Lecture
https://youtu.be/yI_WSIaFbFQ
URL

Notion https://21f1003586.notion.site/Concurrency-Programming-
URL 7a9088ef19fd46c187bc5f562405aa56

Week # 11

An exercise in concurrent programming


A narrow North-South bridge can accomodate traffic only in one direction at a time

When a car arrives at the bridge

Cars on the bridge going in the same direction ⇒ can cross


No other car on the bridge ⇒ can cross (implicitly sets direction)
Cars on the bridge going in opposite direction ⇒ wait for the bridge to be empty

Cars waiting to cross from one side may enter bridge in any order after direction
switches in their favour

When bridge becomes empty and cars are waiting, yet another car can enter in the
opposite direction and makes them all wait some more

Concurrency Programming 1
An example
Design a class Bridge to implement consistent one-way access for cars on the highway

Should permit multiple cars to be on the same bridge at one time (all going in the
same direction)

Bridge has a public method public void cross (int id, boolean d, int s)

id is the indentity of the car

d indicates direction

true is North

false is South

s indicates time taken to cross (milliseconds)


public void cross(int id, boolean d, int s)

Method cross prints out diagnostics

A car is stuck waiting for the direction to change


Car 10 going South stuck at Fri Feb 25 12:42:13 IST 2022

The direction changes


Car 10 switches bridge direction to South at Fri Feb 25 12:42:13 IST 2022

A car enters the bridge


Car 10 going South enters bridge at Fri Feb 25 12:42:13 IST 2022

A car leaves the bridge


Car 10 leaves at Fri Feb 25 12:42:14 IST 2022

Analysis
The “data” that is shared is the Bridge

State of the bridge is represented by two quantities

Number of cars on the bridge — int bcount

Current direction of bridge — boolean direction

The method public void cross(int id, boolean d, int s) changes the state of the bridge

Concurrent execution of cross can cause problems

but making cross a synchronized method is too restrictive

Only one car on the bridge at a time

Problem description explicitly disallows such a solution

Break up cross into a sequence of actions

Concurrency Programming 2
enter — get on the bridge

travel — drive across the bridge

leave — get off the bridge

enter and leave can print out the diagnostics required

Which of these affect the state of the bridge?

enter → increment the number of cars, perhaps change the direction

leave → decrement the number of cars

Make enter and leave synchronized

travel is just a means to let time elapse — use sleep

Code for cross

public void cross(int id, boolean d, int s) {


// Get onto the bridge (if you can)
enter(id, d);

// Takes time to cross the bridge


try {
Thread.sleep(s);
} catch(InterruptedException e) {}

// Get off the bridge


leave(id);
}

Entering the bridge

If the direction of this car matches the direction of the bridge, it can enter

If the direction does not match but the number of cars is > 0, wait

Otherwise, wait() for the state of the bridge to change

Code for enter

private synchronized void enter(int id, boolean d) {


Date date;

// While there are cars going in the wrong direction


while(d != direction && bcount > 0) {
date = new Date();
System.out.println("Card " + id + " going " + direction_name(d) + " stuck at " + date);

// Wait for our turn


try {
wait();
} catch(InterruptedException e) {}
}

Concurrency Programming 3
...
}

private synchronized void enter(int id, boolean d) {


...
while(d != direction && bcount > 0) { ... wait() ...}
...
if(d != direction) { // Switch direction, if needed
direction = d;
date = new Date();
System.out.println("Card " + id + " switches bridge direction to "
+ direction_name(direction) + " at " + date);
}

bcount++; // Register our presence on the bridge


date = new Date();
System.out.println("Car " + id + " going " + direction_name(d) + " enter bridge at" + date);
}

Code for leave

Leaving the bridge is much simpler

Decrease the car count

notify() waiting cars .. provided car count is zero

private synchronized void leave(int id) {


Date date = new Date();
System.out.println("Car " + id + " left at " + date);

// Check out
bcount--;

// If everyone on the bridge has checked out


// Notify the cars waiting on the opposite side
if(bcount == 0) {
notifyAll();
}
}

Summary
Concurrent programming can be tricky

Need to synchronize access to shared resources

while allowing concurrency

Concurrency Programming 4

Thread Safe Collection
Type 📒 Lecture
Date @March 6, 2022

Lecture
4
#

Lecture
https://youtu.be/yDmnozJBKic
URL

Notion https://21f1003586.notion.site/Thread-Safe-Collection-
URL 56a0ffd9d4cb4f31a0d70b2472f85078

Week # 11

Concurrency and collections

monitor bank_account {
double accounts[100];

boolean transfer(double amount, int source, int target) {


if(accounts[source] < amount) {
return false;
}

accounts[source] -= amount;
accounts[target] += amount;

Thread Safe Collection 1


return true;
}

double audit() {
// compute the balance across all accounts
double balance = 0.00;

for(int i = 0; i < 100; ++i) {


balance += accounts[i];
}

return balance;
}
}

Synchronize access to bank account array to ensure consistent updates

Non-interfering updates can safely happen in parallel

Updates to different accounts, accounts[i] and accounts[j]

Insistence on sequential access affects performance

Thread safety and correctness


Thread safety guarantees consistency of individual updates

If two threads increment accounts[i] neither update is lost

Individual updates are implemented in an atomic manner

Does not say anything about sequences of updates

Formally, linearizability

Contrast with serielizability in databases, where transactions (sequences of


updates) appear atomic

Threads safe collections


To implement threads safe collections, use locks to make local updates atomic

Granularity of locking depends on data structure

In an array, sufficient to protect a[i]

In a linked list, restrict access to nodes on either side of insert/delete

Java provides built-in collection types that are thread safe

ConcurrentMap interface, implemented as ConcurrentHashMap

Thread Safe Collection 2


BlockingQueue , ConcurrentSkipList

Appropriate low level locking is done automatically to ensure consistent local


updates

Remember that these only guarantee atomicity of individual updates

Sequences of updates (transfer from one account to another) still need to be


manually synchronized to work properly

Using thread safe queues for synchronization


Use a thread safe queue for simpler synchronization of shared objects

Producer-Consumer system

Producer threads insert items into the queue

Consumer threads retrieve them

Bank account example

Transfer threads insert transfer instructions into shared queue

Update thread processes instructions from the queue, modifies the bank
account

Only the update thread modifies the data structure

No synchronization necessary

Blocking queues
Blocking queues block when ...

we try to add an element when the queue is full

we try to remove an element when the queue is empty

Update thread tries to remove an item to process, waits if nothing is available

In general, use blocking queues to coordinate multiple producer and consumer


threads

Producers write intermediate results into the queue

Consumers retrieve these results and make further updates

Blocking automatically balances the workload

Producers wait if consumers are slow and the queue fills up

Thread Safe Collection 3


Consumers wait if producers are slow to provide items to process

Summary
When updating collections, locking the entire data structure for individual
updates is wasteful

Sufficient to protect access within a local portion of the structure

Ensure that two updates do not overlap

Region to protect depends on the type of collection

Implement using lower level locks of suitable granularity

Java provides built-in thread safe collections

One of these is a blocking queue

Use a blocking queue to coordinate producers and consumers

Ensure safe access to a shared data structure without explicit


synchronization

Thread Safe Collection 4



Graphical Interfaces and Event-
Driven Programming
Type 📒 Lecture
Date @March 22, 2022

Lecture 1
#

Lecture https://youtu.be/A_BV43krS2I
URL

Notion https://21f1003586.notion.site/Graphical-Interfaces-and-Event-Driven-
URL Programming-5b1c1b8d4bc949fbb624221b795744ab

Week # 12

GUIs and Events


Multiple applications simultaneously displayed on screen

Keystrokes, mouse clicks have to be sent to appropriate window

In parallel to main activity, record and respond to these events

Graphical Interfaces and Event-Driven Programming 1


Web browser renders current page

Clicking on a link loads a different page

Keeping track of events


Remember coordinates and extent (size) of each window

Track coordinates of mouse

OS reports mouse click at (x, y)

Check which windows are positioned at (x, y)

Check if one of them is “active”

Inform that window about mouse click

Tedious and error-prone process

Programming language support for higher level events

Run time support for language maps low level events to high level events

OS reports low level events: mouse clicked at (x, y), key 'a' pressed

Program sees high level events: Button was clicked , box was ticked ...

Better Programming Language (PL) support for events


Programmer directly defines components such as windows, buttons, ... that
generate high level events

Each event is associated with a listener that knows what to do

e.g. click Close Window exits the application

Programming language has mechanisms for

Describing what types of events a component can generate

Setting up an association between components and listeners

Different events invoke different functions

Window frame has Maximize, Iconify, Close buttons

Language “sorts” out events and automatically calls the correct function in the
listener

Example

Graphical Interfaces and Event-Driven Programming 2


A Button with one event, press button

Pressing the button invokes the function buttonpush(...) in a listener

interface ButtonListener {
public abstract void buttonpush(...);
}

class MyClass implements ButtonListener {


...
public void buttonpush(...) {
// what to do when a button is pushed
...
}
}

We have set up an association between Button b and a listener ButtonListener m

Nothing more needs to be done

Button b = new Button();


MyClass m = new MyClass();
b.add_listener(m); // Tell b to notify m when pushed

Communicating each button push to the listener is done automatically by the


runtime system

Information about the button push even is passed as an object to the listener

buttonpush(...) has arguments

Listeners can decipher source of event, for instance

Timer
Recall Timer Example

Myclass m creates a Timer t that runs in parallel

Timer t notifies a Timerowner when it is done, via a function timerdone()

Abstractly, timer duration elapsing is an event, and Timerowner is notified when


the event occurs

In the timer, the notification is done explicitly, manually

In the button example, the notification is handled internally, automatically

In our example, Myclass m was itself the Timerowner to be notified

Graphical Interfaces and Event-Driven Programming 3


In principle, Timer t could be passed a reference to any object that implements
Timerowner interface

Summary
Event driven programming is a natural way of dealing with the graphical user
interface interactions

User interacts with object through mouse clicks, etc.

These are automatically translated into events and passed to listeners

Listeners implement methods that react appropriately to different types of events

Graphical Interfaces and Event-Driven Programming 4



Swing ToolKit
Type 📒 Lecture
Date @March 22, 2022

Lecture # 2

Lecture
https://youtu.be/sV4ItidrL8s
URL

Notion https://21f1003586.notion.site/Swing-ToolKit-
URL 3dec12f4f8c140c9b3a9ffadd0ecd5e0

Week # 12

Event driven programming in Java


Swing toolkit to define high-levle components

Built on top of lower level event handling system called AWT

Relationship between components generating events and listeners is flexible

One listener can listen to multiple objects

Three buttons on window frame all report to common listener

One component can inform multiple listeners

Swing ToolKit 1
Exit browser report to all windows currently open

Must explicitly set up association between component and listener

Events are “lost” if nobody is listening

A button that paints its background red


JButton is Swing class for buttons

Corresponding listener class is ActionListener

Only one type of event, button push

Invokes actionPerformed(...) in listener

Button push is an ActionEvent

public class MyButtons {


private JButton b;
public MyButtons(ActionListener a) {
// Set the label on the button
b = new JButton("MyButton");
// Associate a listener
b.addActionListener(a);
}
public class MyListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
...
// When a button is pressed, the code here executes
}
}
public class XYZ {
// ActionListener l
MyListener l = new MyListener();
// Button m, reports to l
MyButtons m = new MyButtons(l);
}
}

Embedding the button inside a panel


To embed the button in a panel — JPanel

First import required Java packages

The panel will also serve as the event listener

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Swing ToolKit 2
public class ButtonPanel extends JPanel implements ActionListener {
...
}

Create the button, make the panel a listener and add the button to the panel

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonPanel extends JPanel implements ActionListener {


private JButton redButton;
public ButtonPanel() {
redButton = new JButton("Red");
redButton.addActionListener(this);
add(redButton);
}
...
}

Listener sets the panel background to red when the button is clicked

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonPanel extends JPanel implements ActionListener {


private JButton redButton;
public ButtonPanel() {
redButton = new JButton("Red");
redButton.addActionListener(this);
add(redButton);
}
public void actionPerformed(ActionEvent event) {
Color color = Color.red;
setBackground(color);
repaint();
}
}

Embedding the panel in a frame


Embed the panel in a frame — JFrame

public class ButtonFrame extends JFrame implements WindowListener {


public ButtonFrame() { ... }
// Implement WindowListener

Swing ToolKit 3
...
}

Corresponding listener class is WindowListener

JFrame generates seven different types of events

Each of the seven events automatically calls a different function in


WindowListener

public class ButtonFrame extends JFrame implements WindowListener {


public ButtonFrame() { ... }
/*
Seven methods required for implementing WindowListener
// Six out of the seven are stubs
*/
...
}

Need to implement windowClosing event to terminate the window

Other six types of events can be ignored

public class ButtonFrame extends JFrame implements WindowListener {


public ButtonFrame() { ... }
// Six out of the seven methods required for
// implementing WindowListener are stubs

public void windowClosing(WindowEvent e) {


System.exit(0);
}
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
}

JFrame is complex, many layers

Items to be displayed have to be added to ContentPane

public class ButtonFrame extends JFrame implements WindowListener {


private Container contentPane;
public ButtonFrame() {
setTitle("ButtonTest");
setSize(300, 200);

Swing ToolKit 4
// ButtonFrame listens to itself
addWindowListener(this);

// ButtonPanel is added to the contentPane


contentPane = this.getContentPane();
contentPane.add(new ButtonPanel());
}
}

main function
Create a JFrame and make it visible

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonTest {


public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new ButtonFrame();
frame.setVisible(true);
});
}
}

EventQueue.invokeLater() puts the Swing object in a separate event despatch


thread

Ensures that GUI processing does not interfere with other computation

GUI does not get blocked, avoid subtle synchronization bugs

Summary
The swing toolkit has different types of objects

Each object generates its own type of event

Create an appropriate event handler and link it with the object

The unit that Swing displays is a frame

Individual objects have to be embedded in panels which are then added to the
frame

Swing ToolKit 5

More Swing Examples
Type 📒 Lecture
Date @March 22, 2022

Lecture
3
#

Lecture
https://youtu.be/c6Z8BNSv9zY
URL

Notion https://21f1003586.notion.site/More-Swing-Examples-
URL a061c1aa699748b1b9c3adbc2c8e6526

Week # 12

Connecting multiple events to a listener


One listener can listen to multiple objects

A panel with 3 buttons, to paint the panel red, yellow or blue

public class ButtonPanel extends JPanel implements ActionListener {


// Panel has 3 buttons
private JButton yellowButton, blueButton, redButton;
public ButtonPanel() {
yellowButton = new JButton("Yellow");
blueButton = new JButton("Blue");

More Swing Examples 1


redButton = new JButton("Red");
...
}

public void actionPerformed(ActionEvent event) {


...
}
}

Make the panel listen to all 3 buttons

public class ButtonPanel extends JPanel implements ActionListener {


// Panel has 3 buttons
private JButton yellowButton, blueButton, redButton;
public ButtonPanel() {
yellowButton = new JButton("Yellow");
blueButton = new JButton("Blue");
redButton = new JButton("Red");

// ButtonPanel listens to all 3 buttons


yellowButton.addActionListener(this);
blueButton.addActionListener(this);
redButton.addActionListener(this);

add(yellowButton);
add(blueButton);
add(redButton);
}
...
}

Determine what colour to use by identifying source of the event

Keep the existing colour if the source is not one of these three buttons

public class ButtonPanel extends JPanel implements ActionListener {


...
public void actionPeformed(ActionEvent event) {
// Find the source of the event
Object source = event.getSource();
// Get current background colour
Color color = getBackground();

if(source == yellowButton) {
color = Color.yellow;
} else if(source == blueButton) {
color = Color.blue;
} else if(source == redButton) {
color = Color.red;
}

setBackground(color);

More Swing Examples 2


repaint();
}
}

Output

Multicasting: multiple listeners for an event


Two panels, each with 3 buttons, Red, Blue, Yellow

import ...;
public class ButtonPanel extends JPanel implements ActionListener {
// Panel has 3 buttons
private JButton yellowButton, blueButton, redButton;
public ButtonPanel() {
yellowButton = new JButton("Yellow");
blueButton = new JButton("Blue");
redButton = new JButton("Red");
...
add(yellowButton);
add(blueButton);
add(redButton);
}
...
}

More Swing Examples 3


Clicking a button in either panel changes the background colour in both panels

Both panels must listen to all six buttons

However, each panel has references only for its local buttons

Associate an ActionCommand with a button

Assign the same action command to both Red buttons ...

import ...;
public class ButtonPanel extends JPanel implements ActionListener {
// Panel has 3 buttons
private JButton yellowButton, blueButton, redButton;
public ButtonPanel() {
yellowButton = new JButton("Yellow");
blueButton = new JButton("Blue");
redButton = new JButton("Red");

yellowButton.setActionCommand("YELLOW");
blueButton.setActionCommand("BLUE");
redButton.setActionCommand("RED");

add(yellowButton);
add(blueButton);
add(redButton);
}
...
}

Choose colour according to ActionCommand

public class ButtonPanel extends JPanel implements ActionListener {


...
public void actionPeformed(ActionEvent event) {
Color color = getBackground();
String cmd = event.getActionCommand();

if(cmd.equals("YELLOW")) {
color = Color.yellow;
} else if(cmd.equals("BLUE")) {
color = Color.blue;
} else if(cmd.equals("RED")) {
color = Color.red;
}

setBackground(color);
repaint();
}
...
}

More Swing Examples 4


Need to add both panels as listeners for each button

Add a public function to add a new listener to all buttons in a panel

public class ButtonPanel extends JPanel implements ActionListener {


...
public void addListener(ActionListener o) {
// Add a common listener for all
// buttons in the panel
yellowButton.addActionListener(o);
blueButton.addActionListener(o);
redButton.addActionListener(o);
}
}

Add both panels to the same frame

public class ButtonFrame extends JFrame implements WindowListener {


private Container contentPane;
private ButtonPanel b1, b2;

public ButtonFrame() {
...
b1 = new ButtonPanel();
b2 = new ButtonPanel();

// Each panel listens to both sets of buttons


b1.addListener(b1);
b1.addListener(b2);
b2.addListener(b1);
b2.addListener(b2);

contentPane = this.getContentPane();
// Set layout to separate out panels in frame
contentPane.setLayout(new BorderLayout());
contentPane.add(b1, "North");
contentPane.add(b2, "South");
}
}

Other elements - checkboxes


JCheckbox : a box that can be ticked

A panel with two checkboxes, Red and Blue

Only Red ticked, background red

Only Blue ticked, background blue

Both ticked, background green

More Swing Examples 5


Only one action — click the box

Listener is again ActionListener

Checkbox state: selected or not

import ...
public class CheckBoxPanel extends JPanel implements ActionListener {
private JCheckBox redBox;
private JCheckBox blueBox;

public CheckBoxPanel() {
redBox = new JCheckBox("Red");
redBox = new JCheckBox("Blue");

redBox.addActionListener(this);
blueBox.addActionListener(this);

redBox.setSelected(false);
blueBox.setSelected(false);

add(redBox);
add(blueBox);
...
}
}

isSelected() returns the current state

public class CheckBoxPanel extends JPanel implements ActionListener {


...
public void actionPerformed(ActionEvent event) {
Color color = getBackground();
if(blueBox.isSelected()) {
color = Color.blue;
} else if(redBox.isSelected()) {
color = Color.red;
} else if(blueBox.isSelected() && redBox.isSelected()) {
color = Color.green;
}

setBackground(color);
repaint();
}
}

Summary
Swing components such as buttons, checkboxes generate high level events

Each event is automatically sent to a listener

More Swing Examples 6


Listener capability is described using an interface

Event is sent as an object — listener can query the event to obtain details
such as event source, action label, ... and react accordingly

Association between event generators and listeners is flexible

One listener can listen to multiple objects

One component can inform multiple listeners

Must explicitly set up association between component and listener

Events are “lost” if nobody is listening

Swing objects are the most aesthetically pleasing, but useful to understand how
GUI programming works across other languages

More Swing Examples 7

You might also like