Review
(and some new information!)
Creating threads
▪ We saw last week that all programs have one thread (created by the
operating system when the program is started) and that you can create
more threads:
public static void main(String[] args) {
Thread t = new Thread(() -> {System.out.println(“Hello world"); String name = "This is "+Thread.currentThread().getName();System.out.println(name);
}, "My new thread");
t.start();
int name = "This is "+Thread.currentThread().getName();System.out.println(name);
}
Main thread
My new thread
Futures and threadpools
▪ We also saw that Futures and threadpools are very useful if you work
with threads that produce a result:
public static int calculate(int a, int b) {int sum = 0;for(int i=a;i<=b;i++) {
sum += i;}return sum;
}
public static void main(String[] args)throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Integer> f1 = executor.submit(()-> calculate(1,500));Future<Integer> f2 = executor.submit(()-> calculate(501,1000));
int sum = f1.get() + f2.get();executor.shutdown();
}
Future.get() waits until a
thread has finished the
calculation for the future.
We give the
threadpool two
tasks to do.
Race conditions
▪ We saw that unexpected things can happen if a thread works with a
variable or object that is modified by another thread at the same time.
▪ Source code:
▪ Possible execution:
Thread 1:
Element newElement=new Element(3);newElement.next=head;head=newElement;
Thread 2:
Element newElement=new Element(4);newElement.next=head;head=newElement;
Thread 2
Element thread2_newElement=new Element(4);
thread2_newElement.next=head;head=thread2_newElement;
Thread 1 overwrites the
change made by thread 2!
Thread 1
Element thread1_newElement=new Element(3);
thread1_newElement.next=head;
head=thread1_newElement;
Race conditions (2)
▪ Be careful: Race conditions can even happen in a single line of code
▪ A line like
i = i + 1; (“Bad example 1” from last week)
consists of three low-level instructions for your computer:
1. Read the value of variable 𝑖
2. Add 1 to that value
3. Store the result in variable 𝑖
▪ With two threads, the following can happen:
Read the value of variable 𝑖
Add 1 to that value
Store the result in variable 𝑖
Read the value of variable 𝑖Add 1 to that value
Store the result in variable 𝑖
Thread 1 Thread 2
Thread 1 overwrites the
change made by thread 2!
Synchronization with monitors
▪ Monitors can be used to protect a critical section of the code, such that
only one thread executes the critical section at a time:
synchronized(someObject) {.........
}
Thread X
Thread X becomes
the owner of the
monitor and can
enter the critical
section Thread Y Thread Z
The other threads
must wait until thread
X leaves the block
When Thread X leaves
the block, one of the
two waiting threads Y or
Z can enter
The synchronized
statement always
needs an object
Where to put the synchronized statement
▪ Different places possible for a synchronized statement
▪ It can be before you call a method:
▪ Or inside a method that you call:
▪ Often, the entire method is synchronized:
synchronized(someObject) {list.add(3);
}
void add(int value) {Element newElement=new Element(value);synchronized(someObject) {
newElement.next=head;head=newElement;
}}
synchronized void add(int value) {Element newElement=new Element(value);newElement.next=head;head=newElement;
}
Visibility
▪ The Synchronized statement also does something else: It guarantees the
visibility of data modifications to threads
▪ Incorrect example:
▪ Will thread 1 ever terminate?
▪ We don’t know! In Java, it is not guaranteed that thread 1 sees
modifications made by thread 2 unless thread 1 and thread 2 synchronize
(for example with a synchronized statement)
someObject.b=true;...while(someObject.b) {
...}
Thread 2
someObject.b=false;
Thread 1
Visibility (2)
▪ Using a synchronized statement is one way to ensure the visibility of
modifications
▪ It is also possible to declare a class member as volatile:
▪ When a class member is volatile, Java guarantees that a thread reading
the variable
will see all previous modifications by other threads
class SomeClass {volatile boolean b;
}
Example: while(someObject.b) { ...
Example: someObject.b=false;
▪ How can a computer execute multiple threads at the same time? Why
don’t threads see modifications made by other threads?
• LINFO1252: Systèmes informatiques
• LINGI2241: Computer architecture and performance
• LINGI2355: Multicore programming
▪ How do you design algorithms and programs with threads? How can
you prove that a program with multiple threads works correctly?
• LINFO1104: Paradigmes de programmation et concurrence
• LINGI2143: Concurrent systems : models and analysis
• LSINF2345: Languages and algorithms for distributed Applications
Classes in java.util.*
▪ Most data structures in java.util.* are not thread-safe: race conditions
can happen!
• ArrayList, LinkedList, HashSet, PriorityQueue, HashMap,...
▪ If you want to work with these classes from multiple threads, you have
to use synchronized-statements in your code
▪ But there already a lot of helper classes and methods that you can use:
// creates a thread-safe map
Map m = Collections.synchronizedMap(new HashMap(...));
// creates a thread-safe list
List list = Collections.synchronizedList(new LinkedList(...));
▪ There are many other methods to create thread-safe sets,
queues, etc.
How doesCollections.synchronizedList work?▪ The method synchronizedList in
List list = Collections.synchronizedList(new LinkedList(...));
returns an object of type SynchronizedList
▪ SynchronizedList is a wrapper class (a design pattern!). It doesn’t contain
any data. It just wraps thread-safe methods “around” a normal list object:
class SynchronizedList {final List list;final Object mutex = new Object();
SynchronizedList(List list) {this.list = list;
}
public void add(int index, E element) {synchronized (mutex) {
list.add(index, element);}
}...
}
mutex = “Mutual
exclusion”
// Code for T2:while(true) {
Picture currentPicture;synchronized(someObject) {
while(picture==null) {try {
someObject.wait();}catch(InterruptedException e) { throw new RuntimeException(“...", e); }
}currentPicture=picture;picture=null;someObject.notify();
}
CompressedPicture p=compress(currentPicture);p.writeToFile();
}
wait() and notify()
// Code for T1while(true) {
Picture currentPicture = takePhoto();synchronized(someObject) {
while(picture!=null) {try {
someObject.wait();}catch(InterruptedException e) { throw new RuntimeException(“...", e); }
}picture = currentPicture;someObject.notify();
}}
Wait until
picture==null
Notify other waiting
threads that picture
is now !=null
Wait until
picture!=null
Notify other waiting
threads that picture is
now ==null
Waiting with timeout
▪ Sometimes, you want to limit the time to wait (BoundedBuffer exercise
on inginious):
▪ The thread stops waiting if:
• another thread calls notify() or notifyAll()
• the time is over
• the waiting is interrupted (InterruptedException)
▪ Don’t forget to test after waiting if the condition you were waiting for is
satisfied.
someObject.wait(2000); // wait maximum 2000ms