程序代写代做 algorithm database data structure jvm clock graph C flex chain cache javaFx assembly concurrency Java Concurrent Programming

Concurrent Programming
CSE 216 : Programming Abstractions Department of Computer Science Stony Brook University Dr. Ritwik Banerjee

A. Singlecoremachinewithoutanyconcurrency
–runs only one application at a time (no more coding
while listening to music)
B. Singleapplicationwithconcurrency – read digital audio off the network and – decompress it and
– manage playback while
– updating display.
• We will be using Java to study concurrent programs, since it provides a very strong framework for multithreaded programming.
– Based on its java.util.concurrent library.
Concurrency

• There are two fundamental units of execution: processes and threads.
• Processes are more of a concern at the OS-level. They are not within the scope of our syllabus.
• In simpler (non-OS) programs, concurrent programming mostly deals with threads.
• A computer system normally has many active processes and threads. This is the case even in a single-core computer.
– In a single core system, only one thread executes at any given moment.
– The single core is shared among processes and threads through time slicing, a feature of the operating system’s implementation.
Processes & Threads

• A process has its own self-contained execution environment.
• Generally, a process will have a complete (and typically private) set of basic runtime resources. In particular, a process has its own memory space.
– Keep in mind that a “process” is not the same as a “program” or an “application”.
–A single application may actually be a whole set of processes working together.
• In an operating system, processes often need to communicate with each other. This is done through an inter-process communication (IPC) protocol.
– This protocol is OS-specific.
• In Java, most virtual machines run as a single process.
• A concurrent Java application will typically work with multiple threads.
– But there is the provision to use a ProcessBuilder object for situations that call for spawning multiple processes.
Processes

• Wecanthinkofathreadasalightweightprocess.
–Creating a new thread requires fewer resources than
creating a new process.
• A thread exists within a process, and every process has at least one thread.
• Unlike processes, threads share the process resources (memory, open files, database access, etc.).
• Using multiple threads can increase efficiency by working on several things in parallel, but this also introduces additional communication issues.
• Javaprograms:
– You start with a single thread called the main thread (and
there are some additional System threads responsible for memory management).
– The main thread can then create additional threads to start off a concurrent Java program.
Threads

A/Synchronous tasks
I.
Synchronous
–Synchronously means “using the same clock”. So, when two instructions are synchronous, they use the same clock and one of the two must happen after the other.
– In other words, the program must wait for the first instruction to finish, before it can move on the other.
II. Asynchronous
– Asynchronously means not “using the same clock”. That is, two asynchronous
instructions are not concerned with being in step with each other.
– In other words, the program can move on from a task t1 to another task t2 before t1 finishes.
5
© 2019 Ritwik Banerjee

Concurrency or parallelism?
• Sometimes, these get used interchangeably, but they mean two different things. The difference may seem subtle, but is important:
• Concurrency is about handling asynchronous tasks, sometimes with non- deterministic behavior.
– This can happen with reactive programming: a user may or may not click a UI button; if the button is clicked, the window must respond in some way even if it was busy displaying something else.
• Parallelism is about the asymptotic efficiency of a program with deterministic behavior.
– Deals with the dependencies among the subtasks of a deterministic task.
– Parallelism is an abstraction, and concurrency is used to implement it.
– Parallelism does not need to be aware of concurrency, even if the engineers who build parallel computing systems must deal with concurrency.
6
© 2019 Ritwik Banerjee

Concurrency model: asynchronous workers
• Therearevariousmodelsofconcurrency,with each model explaining how tasks are distributed among threads (from a general outside perspective).
• Thefirstmodelisthatofasynchronous workers.
– A producer creates tasks somewhere, and a delegator distributes and assigns tasks to workers, who work asynchronously without depending on each other.
– Each worker runs on a different thread
Producer Delegator
Worker 1 Worker 2 Worker 3
7
© 2019 Ritwik Banerjee

Concurrency model: asynchronous workers
• Thisisaconceptuallysimplemodel, but problems crop up because when you share a task among workers, the worker will typically depend on some common shared data.
• Theworkers(i.e.,threads)thatneed access to shared data need to ensure that the changes made by one is visible to the others.
– e.g., changes are pushed to main memory and not in the CPU cache
• Theworkersneedtoavoidshared state concurrency problems like deadlock, race conditions, etc.
Worker 1
Delegator Worker2 Sharedstate Sharedstate in memory in database
Worker 3
8
© 2019 Ritwik Banerjee

Problems with asynchronous models
• Threads can interfere with one another – e.g., one thread is partly done with a computation when another thread decides to access shared data.
class Counter {
private int c = 0;
public void increment() { c++; } public void decrement() { c–; } public int value() { return c; }
}
• Evensimpleoperationslikec++consistofmultiple smaller steps: (1) retrieve current value, (2) increment it,
and (3) store the new value back into the memory location.
• Inotherwords,itisnotanatomicoperation.

Atomicity and critical regions
• An atomic operation is something that cannot be broken down into smaller sub-operations. It either runs completely, or not at all.
• Any part of a program that need to be atomic (for concurrency to work properly) is called a critical area or region of the code.
10
© 2019 Ritwik Banerjee

Problems with asynchronous models
class Counter {
private int c = 0;
public void increment() { c++; } public void decrement() { c–; } public int value() { return c; }
}
• Supposethisprogramisrunontwothreads,AandB,and they both call increment() at around the same time.
• Duetolackofatomicity,theactualsequenceofoperations may be something like
1) A retrieves c.
2) B retrieves c.
3) A increment()s c with the result being 1. 4) B decrement()s c with the result being -1. 5) A stores result in c, so now, c = 1.
6) B stores result in c, so now, c = -1.
Thread interference
leads to the result of A’s work being overwritten by B, and lost.

• A memory inconsistency error occurs when different threads have inconsistent views of what should be the same data.
• Thesecanbeavoidedbyclearlyestablishingwhenone event happens before another, thereby guaranteeing that data written by one operation is visible to the other.
• Again,supposethisisrunontwothreads,AandB,and 1) A increments c using the unary operation, and
2) B executes System.out.println(c);
• IfA’soperationdoesnot“happenbefore”thatofB–and there’s guarantee that it does – the printed value may still
be zero.
• Wewilllaterseehowtoimplementcodetocreatespecific “happens before” relations between two operations.
Problems with asynchronous models

• A race condition is a situation that occurs when a program attempts to perform multiple operations at the same time, but because of the nature of the hardware or software being used, the operations must be done in the proper sequence to be done correctly.
• In order to avoid race conditions, systems often use the concept of a lock (also called mutex, for mutual exclusion), which gives one thread exclusive access to the locked resource.
• But locks must be used carefully! Otherwise, we may adversely affect a concurrent program’s ability to actually work concurrently, correctly, and in a timely manner – a property called liveness.
• The most important enemies of liveness are deadlock, starvation, and livelock.
Problems with asynchronous models

• A deadlock is a state in which each member of a group is waiting for another member (including itself) to take action, such as sending a message or releasing a lock.
A thread t1 holds the lock l1 and wants another lock l2.
Another thread t2 holds the lock l2 and wants l1.
Problems with asynchronous models

• Starvation is a situation where a thread is unable to gain regular access to shared resources and is unable to make progress.
• This happens when a shared resource is made unavailable for long periods of time by “greedy” threads. E.g., if a thread locks a resource for a long time, denying access to other threads.
Problems with asynchronous models

• Livelock is a special type of starvation caused by over- communication, where the threads are so busy conveying messages to each other that they are not doing their actual job!
• Think of two overly polite people walking in opposite directions in a corridor, and when they approach each other
– A moves to his left to let B pass while B moves to her right to let A pass.
– Seeing that they are still blocking each other, A moves to his right, while B moves to her left.
– They’re still blocking each other, so it goes on … neither person willing to become less polite.
• The responses could form a communication chain.
• Or even an infinite cycle.
Problems with asynchronous models

t1
responding to
t2 responding to
t3 … tn
… t1 t3 t2
A livelock can form a long communication chain, or even a cycle.
17
© 2019 Ritwik Banerjee

Concurrency model: asynchronous
workers
• Concurrencymayalsobepartlylost when the threads start waiting for each other to access the shared data.
• Manyconcurrentdatastructuresare blocking, i.e., only one thread can access the data at any given time.
• Thisleadstoastruggleforaccessto the data. Too much of this will effectively lead to serial execution of the part of the code that accesses the shared data.
• Inthispicture,allworkersneedaccess to the shared state, so serial execution will effectively mean that all concurrent is lost, and we might as well forget about multithreading.
Delegator
Worker 1 Worker 2 Worker 3
Shared state Shared state
in memory
in database
18
© 2019 Ritwik Banerjee

Concurrency model: asynchronous workers
• Therearetwowaysaroundthis problem: non-blocking algorithms and data structures (which are really difficult to implement), or persistent data structures.
• Apersistentdatastructureisone that preserves its own immediately previous version upon modification.
• So, if threads t1 and t2 refer to the same persistent object, and t1 changes something, then t1 gets a reference to the new version while t2 sees the older one. Neither thread will see the data structure in an internally inconsistent state.
Exercise: think of a persistent list where only one of the multiple thread adds new elements to the head of the list.
• What data structure would you use to
implement this list?
• If you didn’t worry about multithreading and
persistence, would you have gone for another option? would that option be more efficient for many list operations?
19
© 2019 Ritwik Banerjee

Concurrency model: asynchronous workers
• Inapicturelikethis,“concurrency” means that the order in which tasks (or even statements within the tasks) are completed is non-deterministic.
• Thismakesitdifficulttoestablishif one execution happens before another.
• Andwithoutthis“happensbefore” ordering among events, it becomes difficult to reason about the system’s behavior.
Delegator
Worker 1 Worker 2 Worker 3
Shared state Shared state
in memory
in database
20
© 2019 Ritwik Banerjee

Concurrency model: assembly line
• Theworkersarearrangedlikeanassemblyline in a factory, with each worker doing only a part of the entire work before it passing it along to the next worker in the line.
• Inreality,though,multipleassemblylinesare often used.
• Thesesystemscangetquitecomplex,with situations like a thread t1 from one line depending on threads t2 and t3 from other lines.
Worker 1 Worker 2 Worker 3
Worker 1 Worker 2 Worker 3 Delegator Worker 4 Worker 5 Worker 6 Worker 7 Worker 8 Worker 9
21
© 2019 Ritwik Banerjee

Concurrency model: assembly line
+ Conforms better with underlying hardware since each assembly line works like a single-threaded system.
Workers can keep in memory the data they need to work with, making the system more efficient (I/O operations are typically very slow).
Sequential ordering of subtasks is usually possible.
– Tracing the code runtime can get very difficult because a
single task is spread across multiple workers (i.e., thread instances).
This leads to tracing through multiple function callbacks and callback handlers.
Unlike this, with the asynchronous workers model, a programmer can pretty much read the entire code being executed in one place.
22
© 2019 Ritwik Banerjee

Concurrency model: functional parallelism
• Implementtheprogramusingfunctioncalls,whereeachfunctionisseenas an “agent” who is sending a message to another one (implemented as a function call).
• Allparameterspassedtoafunctionarecopied,sonooutsideentitycan modify the data. This automatically avoids race conditions on shared data.
– Note that this effectively makes each function an atomic operation.
– Therefore, a function call can be executed completely independent of another function call, which in turn means that an algorithm implemented functionally can be parallelized on, say, multiple CPUs.
• There’sacaveat,asalways:
– it’s not always obvious to figure out which function calls to parallelize.
– coordinating function calls across CPUs has a performance overhead, and the functional model of parallelism is not worth it unless the work done by a function is enough to compensate for that overhead.
23
© 2019 Ritwik Banerjee

Next, we will see how these various models of concurrency are implemented in Java, and how we can create multithreaded Java programs that avoid the associated problems like deadlock, starvation, memory inconsistency, etc.

Threads in Java
Direct thread creation & thread management
Create a lava.lang.Thread object whenever the program needs to start an asynchronous task.
Abstraction for thread creation and thread management, separating it from the rest of the program. (Modular code)
There are a few different ways of doing this. E.g., pass the asynchronous task to a
java.lang.concurrent.Executor.
The executor is an interface, and must be implemented according to the task your program needs to accomplish.
25
© 2019 Ritwik Banerjee

26
Thread Creation
12
Implement
java.lang.Runnable
and create an instance of that class.
Extend
java.lang.Thread
and create an instance of that subclass.
© 2019 Ritwik Banerjee

Implementing your own Runnable • Theinterfacejava.lang.Runnableisafunctionalinterfacewithjustone
defined method, run(), whose return type is void.
• TherunnableinstanceispassedtotheThreadconstructor:
public class RunnableImpl implements Runnable {
public void run() {
System.out.println(“A runnable implementation.”);
}
public static void main(String… args) { (new Thread(new RunnableImpl())).start();
} }
27
© 2019 Ritwik Banerjee

Extending the Thread class
• TheThreadclassitselfactuallyimplementsRunnable,butitsrun()
method does nothing.
• So,ifyouwriteasubclassofThread,itshouldprovideitsown
implementation overriding the empty run() method in the superclass. public class ThreadImpl extends Thread {
public void run() {
System.out.println(“A thread subclass implementation”);
}
public static void main(String args[]) { (new ThreadImpl()).start();
} }
28
© 2019 Ritwik Banerjee

public class RunnableImpl implements Runnable {
public void run() {
System.out.println(“A runnable implementation.”);
}
public static void main(String… args) { (new Thread(new RunnableImpl())).start();
Comparing the two ways of creating threads
} }
1
1)
More flexible: the Runnable can
extend a class other that Thread
while implementing an asynchronous task.
– This is a nice abstraction, which decouples the actual task being run, from the object that’s running it.
Extending the Thread class is the easier option for simpler tasks.
– It is not as flexible as the first option, but the Thread class already has a number of methods useful for thread management.
2)
2
29
© 2019 Ritwik Banerjee

30
Pausing a thread
A currently running
thread will suspend
execution if
Thread.sleep(long ) is invoked. The processor time can then be made available to other threads.
It’s an overloaded method:
• Thread.sleep(long milliseconds), and
• Thread.sleep(long milliseconds, int
nanoseconds).
Do not assume that invoking sleep will suspend the thread for precisely that specified amount of time.
• Time facilities are OS- dependent.
• The sleep period can be interrupted by other methods.
public static class SleepMessages {
public static void main(String[] args) throws InterruptedException {
} }
String[] sentences = {“Mares eat oats”, “Does eat oats”,
“Little lambs eat ivy”,
“Kids will eat ivy too”}; for (String s : sentences) {
Thread.sleep(4000); // suspend execution for 4 seconds
System.out.println(s); }
© 2019 Ritwik Banerjee

Pausing a thread
• Whenasleepingthreadisinterrupted,itthrowsInterruptedException. In this example, there is no need to catch it since there is only one thread.
• InterruptioncanbeinvokedbyThread.interrupt().Thisismeantto indicate that the thread should stop what it’s doing and do something else.
– What is this “something else”? Up to the programmer.
– A common practice is to simply terminate the thread.
– For interruption to work properly, a thread must support its own interruption.
public static class SleepMessages {
public static void main(String[] args) throws InterruptedException {
} }
String[] sentences = {“Mares eat oats”, “Does eat oats”,
“Little lambs eat ivy”,
“Kids will eat ivy too”}; for (String s : sentences) {
Thread.sleep(4000); // suspend execution for 4 seconds
System.out.println(s); }
31
© 2019 Ritwik Banerjee

How does a thread support its own interruption?
• Generallyspeaking,thiswilldependonwhatthethreadisdoing.Acommon practice is to simply return from the run() method:
public class SleepMessages {
public static void main(String[] args) throws InterruptedException {
String[] sentences = {“Mares eat oats”, “Does eat oats”,
“Little lambs eat ivy”,
“Kids will eat ivy too”}; for (String s : sentences) {
try {
Thread.sleep(4000); // suspend execution for 4 seconds
} catch (InterruptedException e) {
return; // no more printing once interrupted
}
System.out.println(s); }
} }
32
© 2019 Ritwik Banerjee

public class JavaFXThreadInterrupt extends Application { private static boolean _first_click = true;
@Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
Button resetButton = new Button(“Start Counter”);
Thread countingThread = new Thread(new Runnable() { @Override public void run() {
for (int i = 0; ; i++) { try {
} }
});
System.out.printf(“%d “, i);
Thread.sleep(1000);
} catch (InterruptedException e) {
i = 0;
System.out.println(); }
Thread interruption example

} }
resetButton.setOnMouseClicked(e -> { resetButton.setText(“Reset Counter”); if (_first_click) {
countingThread.start();
_first_click = false; } else
countingThread.interrupt(); borderPane.setCenter(resetButton);
Scene scene = new Scene(borderPane, 400, 200); primaryStage.setScene(scene); primaryStage.setTitle(”Counting Interruption Example”); primaryStage.show();
});
Thread interruption example

Thread priorities and scheduling
• InaJavaapplication,mainisathreadonitsown.
• Oncemultiplethreadsarerunning,theruntime thread scheduler decides which thread is scheduled to run.
• Thisisdecidedbasedonthepriorityofathread (relative to other threads in the application).
• Thepriorityofathreadisaconstantintranging from MIN_PRIORITY (= 1) to MAX_PRIORITY (= 10).
– The default value is NORM_PRIORITY (= 5)
• Whenathreadiscreatedbyanother,itinherits
the priority of the creator thread.
• TheRunnablethreadwiththehighestpriorityis selected to run. Only when this thread stop()s, yield()s (or becomes un-runnable in some other way) does a thread with lower priority get to run.
35
© 2019 Ritwik Banerjee

Thread priorities and scheduling
• Ifmultiplethreadswiththesamepriorityareready to run, the scheduler picks one randomly, and the chosen thread will run until one of the following happens:
– a higher priority thread becomes Runnable
– this thread stop()s, yield()s or its run()
method finishes running and exits.
– the time-slicing of the OS decides this thread’s time allocation is over.
• Atanygiventime,thehighestprioritythreadis running. But there is no guarantee! The thread scheduler may some times run a lower-priority thread to avoid starvation.
As a programmer, you should use priorities for efficiency, but never exclusively rely on them for algorithmic correctness.
36
© 2019 Ritwik Banerjee

State transition diagram
• This is a slightly simplified picture. For details, see the Thread.State enumerable type.
37
© 2019 Ritwik Banerjee

State transition diagram
• In the new state, the thread has not been start()ed, and therefore, is not considered to be Runnable yet.
• In this state, the thread scheduler doesn’t know about this thread.
38
© 2019 Ritwik Banerjee

State transition diagram
• In the runnable state, the thread may not actually be running, but it is known to the thread scheduler, and it can be scheduled to run immediately.
• At any given point in time, multiple threads can simultaneously be in this state.
39
© 2019 Ritwik Banerjee

State transition diagram
• Lastly, a thread arrives at the dead state when its run() method exits.
• Once here, the thread can’t be scheduled to run again.
• You may see stop() being used to reach this state, but it’s a deprecated method, so please avoid it!
40
© 2019 Ritwik Banerjee

private class Wool extends Thread {
@Override
public void run() {
System.out.printf(“Running thread %d.%n”, Thread.currentThread().getId()); try {
} }
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); }
/** Put break points and use the debugger to see how many threads are running. */
public static void main(String… args) {
Wool t = new Wool();
System.out.printf(“Running thread %d.%n”, Thread.currentThread().getId()); t.run();
t.start();
}
Spawning a new thread
• We saw how to create a new thread (either by extending the Thread class, or by implementing the Runnable interface).
• But that alone doesn’t ensure that the task will actually be running as a separate thread!
41
© 2019 Ritwik Banerjee

Joining threads
42
© 2019 Ritwik Banerjee

Concurrent Implementations
• We have discussed some of the issues plaguing asynchronous tasks, and example/overview of simple multithreaded Java programs.
• It’s time to put the two together.

Creating the “happens before” relation
Invoke a thread’s start() method.
• Everything that happens before this invocation is within a single thread, so the transactions are executed in the order of the code (i.e., sequentially, line by line).
• Every statement before calling start() happens before every statement in the newly spawned thread.
By using synchronization.
• This prevents thread interference and memory inconsistency errors.
• But if used incorrectly, can cause thread contention issues including starvation, livelock, and deadlock.
• There are some specific types of inter-thread synchronization that happen when threads wait for each other. We will see this later in the join(), notify(), and wait() methods.
44
© 2019 Ritwik Banerjee

Synchronization
Synchronization is built around an entity called the intrinsic lock or the monitor lock.
Every object has an intrinsic lock associated with it.
If a thread needs exclusive access to an object, it acquired that object’s intrinsic lock. After accomplishing its task, it releases the lock.
There are other types of lock too. E.g., a ReentrantLock.
45
© 2019 Ritwik Banerjee

Synchronized methods
• Just add the synchronized keyword to the method declaration.
• Each call to a synchronized method is atomic.
• When one thread is executing a synchronized method for this, all other threads that invoke synchronized methods for the same object suspend execution until the first thread is done.
• When a synchronized method exits, it establishes a happens before relation with any future invocation of a synchronized method for the same object.
public class SynchronizedCounter { private int c = 0;
public synchronized void increment() { c++; }
public synchronized void decrement() { c–; }
public synchronized int value() { return c;
}
}
46
© 2019 Ritwik Banerjee

Synchronized blocks
• Codeblocksthatarenotmethodscanbe synchronized too.
• Asynchronizedstatementcanbeusedtoacquirea lock on any object, not just this, when executing a block of the code in a method:
synchronized (expr) { /* code block */
}
• Insuchacode,exprmustevaluatetoanobject reference.
– If that object is already locked by another thread, the thread executing this code block remains blocked until the lock is released.
– When the thread executing this code block obtains the lock on the object, the statements in the synchronized block are executed.
– This execution is atomic.
– Once the work is done, the thread releases the lock.
47
© 2019 Ritwik Banerjee

48
The Dining Philosophers problem
Five (in general, n) silent philosophers sit at a table around a bowl of spaghetti, and a fork is placed between each pair of adjacent philosophers.
• Aphilosophermusteitherthinkoreat(can’tdobothat the same time).
• Aphilosophercanonlyeatspaghettiwhenhehasboth left and right forks.
• Aphilosophercangrabtheforkonhisrightortheoneon his left as they become available, but can’t start eating before getting both of them.
• Thepossibilityofadeadlock:ifall5philosopherspick up the left fork at the same time and wait until the right fork is available, then no progress is possible again (starvation).
• ThiswasoriginallyformulatedbyDijkstra,whogavethis as an exam problem in 1965.
© 2019 Ritwik Banerjee

Inter-thread communication & coordination
• Communication and coordination always happens through some ‘language’ that serves as a common medium.
• Threads, too, need to communicate, and this is done through the ‘language’ of certain conventions. Without these, we may end up in the worrisome situations (e.g., livelock).
• One common convention is the guarded block, which keeps checking for some condition to be satisfied.
– A thread may run only when that condition is, indeed, satisfied.
– It’s a simple enough concept, but we must be careful with the sequence of steps to follow when implementing this convention.
49
© 2019 Ritwik Banerjee

Guarded blocks
• Let’ssaywehaveamethod guardedJoy() that must not proceed unless a Boolean shared variable joy is set to true by another thread.
• It’sasimpleconcept.Wedon’tevenneed to know any implementation related to threads.
• Ofcoursewecanguardagainsta condition with a loop, but this wastes CPU cycles.
– Which is exactly what we want to avoid in multithreaded applications!
– Don’t do this!
public void guardedJoy() {
while (!joy) { /* do nothing */ } System.out.println(“I am happy now!”);
}
50
© 2019 Ritwik Banerjee

Guarded blocks
• If the thread that changes joy could notify our thread once the change is made, then we can avoid checking the value constantly in a loop, and instead just wait.
• There are two methods to do precisely that: notify() and wait(), defined for all objects. A more efficient guard will use these.
public synchronized void guardedJoy() { while (!joy) {
try { wait(); }
catch (InterruptedException e) {
/* do nothing */
} }
System.out.println(“I am happy now!”);
System.out.println(”And efficient!”); }
51
© 2019 Ritwik Banerjee

Waiting
Why do we need the try-catch inside the loop? Can’t we just wait() and then print inside the catch block?
• Other threads may issue notifications for many things,
not just the variable our current thread cares about.
• An InterruptedException may occur even if joy remains unchanged.
• This guard loops once per notification, but it will still be orders of magnitude more efficient than our first code (in terms of CPU cycles).
public synchronized void guardedJoy() { while (!joy) {
try { wait(); }
catch (InterruptedException e) {
/* do nothing */
} }
System.out.println(“I am happy now!”);
System.out.println(”And efficient!”); }
52
© 2019 Ritwik Banerjee

Waiting
Why do we need the method
to be synchronized?
• Noticethatwait()isan
instance method. So the question is, which object’s wait() are we calling?
• Wearecalling this.wait(), and a
thread calling it must own the intrinsic lock of this.
• Otherwise,anexception gets thrown.
public synchronized void guardedJoy() { while (!joy) {
try { wait(); }
catch (InterruptedException e) {
/* do nothing */
} }
System.out.println(“I am happy now!”);
System.out.println(”And efficient!”); }
53
© 2019 Ritwik Banerjee

Waiting
Why did the language designers want wait() to only be allowed when the thread calling it owns the object’s intrinsic lock?
• The wait() method actually releases the lock
while it waits, so that other threads can acquire the lock and do their job.
• When another thread finishes its job, it will issue a notification.
• That, in turn, will interrupt our thread and make it check the value of joy.
public synchronized void guardedJoy() { while (!joy) {
try { wait(); }
catch (InterruptedException e) {
/* do nothing */
} }
System.out.println(“I am happy now!”);
System.out.println(”And efficient!”); }
54
© 2019 Ritwik Banerjee

Notifications
• Notificationscanbesentbyathreadintwoforms: notify() and notifyAll().
• Thesearealsoinstancemethods.Thatis,athreadis invoking them in the form of obj.notify() or obj.notifyAll(), for some object obj.
• ThethreadrunningthespreadJoy()methodwill
notify every thread that is waiting on the intrinsic lock of this object.
public synchronized void spreadJoy() { joy = true;
notifyAll(); }
55
© 2019 Ritwik Banerjee

• Notifications can be sent by a thread in two forms: notify() and notifyAll().
• The notify() method, in contrast, wakes up a single thread out of possibly many waiting on this object’s intrinsic lock.
– The choice of the thread is random.
– This is useful only in massively parallel applications where a very large number of threads are all performing similar tasks.
– We don’t necessarily care which specific thread gets woken up, but we don’t want all those threads to be interrupted when, finally, only one of them will actually be scheduled.
– The cost of that many interruptions (because the number of threads is so high) is not worth it.
Notifications

The producer- consumer model through an example
• In this example, our data is going to be a series of text messages.
• The producer creates the data, and the consumer takes that data and does something with it.
• The two threads communicate using a shared object.
– Communication is critical to avoid many invalid scenarios, e.g., the consumer trying to retrieve the data before it has been produced!
• To model the shared entity used by the producer and the consumer threads, we are going to create an object called Drop.
57
© 2019 Ritwik Banerjee

public static class Drop {
/** The message sent from producer to consumer */
private String message;
/**
* A status that is true if the consumer should wait for the
* producer to send a message, and false if the producer should * wait for the consumer to retrieve a message.
*/
private boolean empty = true; // cont. next slide
58
The shared object
© 2019 Ritwik Banerjee

/**
* This method waits until the next message is available. It then toggles the
* {@link #empty} status and issues a notification that the message is now
* available. *
* @return the currently available message. */
public synchronized String take() { while (empty)
try { wait(); } catch (InterruptedException ignore) { /* do nothing */ } empty = true;
notifyAll();
return message; }
// cont. next slide
59
The shared object
© 2019 Ritwik Banerjee

/**
* This method waits until a message has been retrieved. It then toggles the
* {@link #empty} status, stores the next message, and issues a notification
* that the next message is now available for retrieval. *
* @param message the current message to be retrieved next by the consumer. */
public synchronized void put(String message) { while (!empty)
try { wait(); } catch (InterruptedException ignore) { /* do nothing */ } empty = false;
this.message = message;
notifyAll(); }
}
60
The shared object
© 2019 Ritwik Banerjee

public class Producer implements Runnable { protected static final String DONE = “done”; private Drop drop;
public Producer(Drop drop) { this.drop = drop; }
public void run() {
Random r = new Random();
List messages = Arrays.asList(“Mares eat oats.”,
The
producer thread
messages.forEach((String msg) -> { drop.put(msg);
try { Thread.sleep(r.nextInt(5000)); }
catch (InterruptedException ignore) { /* do nothing */ } });
drop.put(DONE); }
}
“Does eat oats.”,
“Little lambs eat ivy.”, “Kids will eat ivy too.”);
61
© 2019 Ritwik Banerjee

public class Consumer implements Runnable {
private Drop drop;
public Consumer(Drop drop) { this.drop = drop; }
public void run() {
Random r = new Random();
for (String msg = drop.take(); !msg.equals(Producer.DONE); msg = drop.take()) {
} }
}
System.out.printf(“MESSAGE RECEIVED: %s\n”, msg); try {
Thread.sleep(r.nextInt(5000));
} catch (InterruptedException ignore) {/* do nothing */ }
62
The consumer thread
© 2019 Ritwik Banerjee

public static void main(String… args) { Drop drop = new Drop();
(new Thread(new Producer(drop))).start(); (new Thread(new Consumer(drop))).start();
}
The main thread
63
© 2019 Ritwik Banerjee

public static void main(String… args) { Thread t1 = new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException ignore) { /* do nothing */
}
System.out.println(“Running t1.”); });
Thread t2 = new Thread(() -> { try {
Thread.sleep(new Random().nextInt(4000)); } catch (InterruptedException ignore) {
Thread joins
• The calling thread waits (i.e., pauses its own execution) until the referenced thread dies.
• To understand its effects, let’s look at this code, which doesn’t join the threads at all.
• This is a possible output:
/* do nothing */
}
System.out.println(“Running t2.”); });
t1.start(); t2.start();
System.out.println(“Running the main thread.”); }
64
© 2019 Ritwik Banerjee

Thread joins
• The same code with joins:
– the main thread creates and starts t1 and t2
– the two threads t1 and t2 may run concurrently
– the main thread waits for t1 to finish running (if t1 finished running before join() was called, then the call to join() will return immediately to the main thread)
– it also waits for t2 to finish running (it is possible that this happens before t1 finished running)
– no matter the order of t1 and t2, it is the main thread that is waiting for both to finish.
• Now, the output will always be
public static void main(String… args) throws InterruptedException { Thread t1 = new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException ignore) { /* do nothing */
}
System.out.println(“Running t1.”);
});
Thread t2 = new Thread(() -> {
try {Thread.sleep(new Random().nextInt(4000)); } catch (InterruptedException ignore) {
/* do nothing */
}
System.out.println(“Running t2.”); });
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println(“Running the main thread.”); }
65
© 2019 Ritwik Banerjee

At least two of the following threads are run at any given time:
1) TheJavaFXApplicationthread.Any“live” scene, i.e., a scene that is a part of a window, must be accessed from this thread, and this thread alone.
2) ThePrismrenderingthread.Ithandles rendering separately from the event dispatcher, and allows the ith frame to be rendered while the
! + 1 st frame is being processed.
3) TheMediathread.Thisthreadrunsinthe background and synchronizes the latest frames through the scene graph by using the JavaFX application thread.
Multithreading in JavaFX

• The UI design begins with a Stage instance.
– But the actual rendering depends on the platform on which it is
deployed (web page, desktop, tablet, etc.)
• The Scene instance is rendered on the stage, where various Node instances may visually interact with each other. This scene is the container for all the content in a scene graph.
• The scene graph is, however, not thread-safe!
• The scene graph executes on the JavaFX Application thread, which also processes all the events. Thus, any “live” manipulation on the scene must be done on the Application thread, and nowhere else.
– The only exceptions would be nodes that are not attached to the live scene.
– If a non-Application thread manipulates objects in a live scene, the results may be wrong. But on the other hand, any long-running task on the Application thread will freeze the UI.
• Because of all these reasons, JavaFX cannot be made thread-safe simply by synchronizing its threads’ tasks. We need some other approach!
Multithreading in JavaFX

Thread confinement
JavaFX uses thread confinement, a program design or technique where only one thread can access the thread-unsafe part of the code.
• A simple way to achieve thread-safety because any data in thread confinement (which is call thread-local) avoids race conditions because it doesn’t even need to be locked.
Java implements this using a ThreadLocal object.
• It simulates a single object to be stored on a per-thread basis, with get() and set() methods.
• The get() method always returns the most updated value passed to set() from the currently executing thread.
68
© 2019 Ritwik Banerjee

public static void main(String… args) { ThreadLocal threadMessage = new ThreadLocal<>();
(new Thread(() -> {
threadMessage.set(“Thread in runnable1”); try {
Thread.sleep(3000);
System.out.println(threadMessage.get());
} catch (InterruptedException e) { e.printStackTrace(); }
})).start();
(new Thread(() -> {
threadMessage.set(“Thread in runnable2”); try {
Thread.sleep(1000);
threadMessage.set(“The message in runnable2
changed”);
} catch (InterruptedException e) { e.printStackTrace(); }
Thread.sleep(1000); System.out.println(threadMessage.get());
})).start();
(new Thread(() -> {
threadMessage.set(“Thread in runnable3”); try {
Thread.sleep(3000);
System.out.println(threadMessage.get());
} catch (InterruptedException e) { e.printStackTrace(); }
})).start(); }
Thread confinement
Run this code to see what happens with a thread-local String object, even when it is accessed and modified by multiple threads.

Running “later”
• ToavoidafrozenandunresponsiveGUI,long- running tasks are delegated to separate threads to free the main Application thread.
• Evenso,theGUImustbeupdatedaspertheresults of those tasks, once the other threads finish their computations.
• Thisisdonethroughthe Platform.runLater(Runnable) method.
• Theargumenttothismethod,arunnabletask,isrun on the main Application thread at some unspecified time in the future.
• Thismethod,canbecalledfromanythread,which will push the runnable to a queue and return immediately to the caller.
– We can use this to make the calling thread “post” some task to the main Application thread.
– The Application thread can deal with it “later”, as and when its resources permit.
70
© 2019 Ritwik Banerjee

Running “later”
public class JavaFXRunLaterExample extends Application {
private double number = Math.abs(100d + Math.random()); private final Text text = new Text(Double.toString(number));
private void incrementCount() {
number = Math.sqrt(number); text.setText(Double.toString(number));
}
@Override
public void start(Stage primaryStage) { StackPane root = new StackPane(); root.getChildren().add(text);
Scene scene = new Scene(root, 200, 200);
// heavy computation is running on a separate thread
Thread t = new Thread(() -> {
Runnable updater = this::incrementCount; while (true) {
try { Thread.sleep(1000); }
catch (InterruptedException ignore) { } Platform.runLater(updater);
} });
// so thread doesn’t prevent JVM from shutting down
t.setDaemon(true);
t.start(); primaryStage.setScene(scene); primaryStage.show();
} }
71
© 2019 Ritwik Banerjee