Teaching team
Why concurrent programming?
In general
In this course
Gentle start
Where is John von Neumann?
Using the processor efficiently in the presence of I/O
Operating systems
Distributed systems
Real-time systems
Modeling inherently concurrent systems
The world is concurrent!
Multi-core machines
Cell phones
Laptops
Performing computationally expensive tasks using several processors
Parallel
Concurrent
The book covers parallel programming too – but we will briefly focus on it in the course
Introduction to the problems common to many computing disciplines:
Appreciation of the problems of concurrent programming
Understanding of a range of programming language constructs for concurrent programming
Ability to apply these in practice to synchronisation problems in concurrent programming
Practical knowledge of the programming techniques of modern concurrent programming languages
About the course activities
Two lectures per week
Six supervision/exercise hours
Optional weekly exercise classes. Attend at most one, with your lab partner
Assignments
Intended to answer most basic questions
Email tda382@googlegroups.com
Introduction to concurrent programming
Basic understanding
Synchronisation problems
Introduction to programming languages
Buy @ Cremona!
The message must be flashed every three seconds (For the example, you need the JFLash class)
import javax.swing.*; public class Main6 { private JFlash window; private Thread buyThread; private final int buy_pause = 3000; public Main6() { window = new JFlash("Cremona"); SwingUtilities.invokeLater(window); buyThread = new Thread() { public void run() { while (true) { window.flash("Buy @ Cremona!"); try { Thread.sleep(buy_pause); } catch (InterruptedException e) {} } } }; buyThread.start(); } public static void main(String[] args) { new Main6(); } }
The program does not increase sales as predicted. A psychologist is called in to help!
Free beer!
final int buy_pause = 3000; final int beer_pause = 5000; int next_buy = buy_pause; int next_beer = beer_pause; ... buyThread = new Thread() { public void run() { while (true) { if (next_buy < next_beer) { try { Thread.sleep(next_buy); } catch (InterruptedException e) {} ; window.flash("Buy @ Cremona"); next_beer = next_beer - next_buy; next_buy = buy_pause; } else if (next_buy > next_beer) { try { Thread.sleep(next_beer); } catch (InterruptedException e) {} ; window.flash("Free beer!"); next_buy = next_buy - next_beer ; next_beer = beer_pause ; } else { try { Thread.sleep(next_buy); } catch (InterruptedException e) {} ; window.flash("Buy @ Cremona! - Free beer!"); next_buy = buy_pause ; next_beer = beer_pause ; } } } }
A more natural solution is to run the two simple algorithms concurrently:
final int buy_pause = 3000; final int beer_pause = 5000; buyThread = new Thread() { public void run() { while (true) { window.flash("Buy @ Cremona!"); try { Thread.sleep(buy_pause); } catch (InterruptedException e) {} } } }; beerThread = new Thread() { public void run() { while (true) { window.flash("Free beer!"); try { Thread.sleep(beer_pause); } catch (InterruptedException e) {} } } };
Java threading framework
Thread
class provides the API and generic behaviours A concrete thread
must provide a run()
method which is the code that the thread will execute
when startedProviding thread run()
method
class Buy extends Thread { //some init public void run() { while (true) { window.flash("Buy @ Cremona!"); //add napping here } } }
Runnable
class Buy implements Runnable { //some init public void run() { while (true) { window.flash("Buy @ Cremona!"); //add napping here } } }
buyThread = new Thread() { public void run() { while (true) { window.flash("Buy @ Cremona!"); //add napping here } } };
Invoking the run()
method in a new thread
Inheritance
buyThread = new Buy(…); buyThread.start();
Interface
buyThread = new Thread(new Buy(…)); buyThread.start();
Anonymous classes
buyThread = new Thread(public void run() {...}); buyThread.start();
A sleeping thread can be interrupted, hence the need for the catch/try clause
try { Thread.sleep(milliseconds); } catch (InterruptedException e) { //Panic: do something here! }
Using such languages we are going to
Explore concurrency problems and solutions
Understand how modern programming languages support concurrent programming
Main course programming languages
Java
Erlang
The job of switching between threads is performed by the scheduler
Part of the run-time system, or
Performed using the operating system’s processes and scheduler
Many different methods of scheduling exist
Cooperative scheduling
Preemptive scheduling
a thread is interrupted in order to let other threads continue (e.g. time-slicing)
Erlang have a preemptive scheduler
Most modern JVM’s are also preemptive
Independent
Relatively rare; Rather uninteresting
![]() |
![]() |
![]() |
Competing
Typical in OS and networks, due to shared resources
![]() |
![]() |
![]() |
Deadlock
![]() |
![]() |
![]() |
![]() |
![]() |
Starvation
![]() |
![]() |
![]() |
||
![]() |
Cooperating
Processes combine to solve a common task
Synchronization
![]() |
![]() |
![]() |
![]() |
||
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
An atomic action is something that is guaranteed to execute without interruption
Since the execution of different threads is interleaved, what are the atomic actions?
Single instructions?
Basic code blocks?
Answer: might not specified by the language design. We have to assume the worst! Context switch can occur anywhere, also in the middle of a statement.
What if flash is not atomic for the Cremona display?
while (true) { window.flash("Buy @ Cremona!"); try { Thread.sleep(buy_pause); } catch (InterruptedException e) {} }
while (true) { window.flash("Free beer!"); try { Thread.sleep(beer_pause); } catch (InterruptedException e) {} }
How many people have entered Liseberg at any given time?
Each entrance has turnstiles which record when a person enters or leaves
public class Liseberg { private int counter = 0 ; private Thread east ; private Thread west ; public void done() { System.out.println("Counter: "+counter); } public void enter() { counter++; } public void people (String x) { for(int j = 0; j<100; j++) { try { Thread.sleep(500 + (int)(Math.random()*1000)) ;} catch (InterruptedException e) {} ; System.out.println("Process "+x+" enters "+j); enter(); } ; done() ; } public Liseberg () { east = new Thread() { public void run() { people("East") ; } } ; west = new Thread() { public void run() { people("West") ; } } ; east.start() ; west.start() ; } public static void main(String[] args) { new Liseberg(); } }
To appreciate better the lack of atomicity, change the lines 15-18 by the following ones
for(int j = 0; j<1000000; j++) { //try { Thread.sleep(500 + (int)(Math.random()*1000)) ;} //catch (InterruptedException e) {} ; //System.out.println("Process "+x+" enters "+j);
We expect the answer 200
It depends on the counter++
operation being atomic
Let us disassemble Liseberg.class
(we use the decompiler Jad for that)
public void enter() { counter++; }
0:aload_0 1:dup 2:getfield #6 Field int counter 5:iconst_1 6:iadd 7:putfield #6 Field int counter 10:return
From, above line 2
gets the content of the field counter
in the stack
and line 7 stores counter+1
back into that field
A program executes a sequence of atomic actions
A state is the value of the program variables at any point in time
A trace (or history) is a sequence of states that can be produced by the sequence of atomic actions of a program
Turnstile East | Turnstile West |
---|
counter = 0
0:aload_0 |
|
0:aload_0 |
counter = 0
7:putfield #counter 10:return |
counter = 1
7:putfield #counter 10:return |
counter = 1
A property
of a program is a logical statement that is true for every possible
trace
Two kinds of property are usual for stating correctness properties of concurrent programs
Safety property : a trace never enters a bad state
Liveness property: every trace eventually reaches a good state
Examples of safety properties
The program never produces a wrong answer
An invariant like (x + y < 2)
Examples of liveness properties
The thread terminates
The thread eventually calls a certain procedure
Synchronisation is the restriction of the traces of a concurrent program in order to guarantee certain safety properties
We will see at least two kinds of synchronisation:
Mutual exclusion
The bad traces in the Liseberg problem are caused by the code that
implements counter++
To fix the problem it must be executed atomically
Critical section
It is the property that only one thread can execute in a given piece of code at any given time
How can we achieve it?
Theory: possible with just shared variables
Practice: programming language features (semaphores, monitors, etc.)
Today’s lecture
Next time