Survey
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
Distributed Systems
2. Java Threads
Simon Razniewski
Faculty of Computer Science
Free University of Bozen-Bolzano
A.Y. 2014/2015
Why processes and threads
• Several actions within a program
– send and receive at the same time
– reaction to user input while doing expensive steps
(e.g. network communications, calculations, disk
fetching)
• Examples:
– Browser
– Skype
– Music/video streaming
Process
• What is a process?
• The unit of computation inside an Operating
System
• A running program with its data and resources
–
–
–
–
–
–
Code (text) of the program
Data: global variables
Program Counter
CPU registers
Stack: parameters, local variables
Resources: open files, network connections, I/O, …
• JVM runs as a single process
Process
• What is a process?
• The unit of computation inside an Operating
System
• A running program with its data and resources
–
–
–
–
–
–
Code (text) of the program
Data: global variables
Program Counter
CPU registers
Stack: parameters, local variables
Resources: open files, network connections, I/O, …
• JVM runs as a single process
Process States
Active process
assign CPU
creation
ready
running
termination
revoke CPU
init
wait for event
event
terminated
blocked
• Multitasking system multiple running processes
• Single processor one active process
– Time sharing, context switch
Thread
• Lightweight process: smallest unit of computation that
can be scheduled in an O.S.
• Shares code and data with other associated threads
– Task: set of threads that refer to the same code and data
– Process = task with (at least) a single thread!
• Task taken as synonym of process
• It is a single control-flow within a process
• Multithreading like Multitasking
• Execution of Java program: JVM creates a thread that
executes the main method
– Other threads can be dynamically created
Thread: Features
• Memory sharing: threads share variables
– No reserved memory for data and heap: all
threads in a process share the same address space
• Private stack and program counter
• Low context switch cost
– No need to handle code&data
– Much more performing than for processes
• But… privacy/synchronization issues: threads
of the same task can modify each other’s data
Single
vs Multithreaded
Processes
Processi
single- o multi-threaded
Sistemi Operativi T AA 2009-2010
31
Java Thread: Big Picture
Process
Code
Static variables
Thread 3
Memory
Stack 3
local var., methods
PC 3
Thread 1
Stack 1
local var., methods
Thread 2
PC 1
Stack 2
local var., methods
PC 2
Implementation
• java.lang.Thread class
• Provides the functionalities for a thread
• With new(), the thread is created but is still
not active
• start() activates the thread
• start() calls run(), empty method
– Extend Thread and override run() in order to
implement a thread!
Simple Skeleton
class SimpleThread extends Thread {
public void SimpleThread()
{ <constructor> }
public void run() {
<behavior of each instance of SimpleThread>
}
}
public class SimpleMain
{
public static void main (string[] args)
{
Thread t1=new SimpleThread();
t1.start(); //Thread t1 activated … don’t call run() directly!
… //HOW MANY THREADS HERE?
}
}
A Thread not Thread
• Is it possible to define a JAVA thread that does
not extend Thread?
– JAVA does not support multiple inheritance
• Interface Runnable: a class denoting instances
that can be run by a thread
• How-to
– Implement Runnable and its run() method
– Create a new object of this class
– Create a new Thread passing the object as
parameter of the constructor
– start() thread
Simple Skeleton
class SimpleRunnable
extends MyClass
implements Runnable {
public void run() {
<behavior here…>
}
}
public class AnotherSimpleMain {
public static void main(String args[]){
SimpleRunnable r = new SimpleRunnable();
Thread t = new Thread(r);
t.start();
}
}
Thread Lifecycle
• New Thread
– Thread created with new
– Still inactive
• Runnable = executable (could be in execution)
• Not runnable
– Cannot be currently scheduled for the execution
• Dead
– Thread has autonomously finished its execution (end of run())
– stop() method invoked on it
Not Runnable – Why?
• Waiting for I/O operation to terminate
• Trying to access to synchronized
object/monitor queued
• One of these methods has been invoked on it
– sleep()
– wait()
– suspend()
• Deprecated because prone to deadlock
• We will discuss these cases in detail…
Stopping a Thread
• Idea: stop the execution of a thread
• stop() is deprecated because unsafe
– We will see the notion of (un)locking
– Basically, stop() causes the thread to
immediately release all resources (unexpectedly)
– Objects protected by the lock could be in an
inconsistent state, and now accessed by other
threads
• In general, google “Java Thread Primitive
Deprecation”
How to Stop a Thread
• Using a recurring pattern (we will re-use it later on)
private Thread myself;
public void stop() { //user-defined!
myself = null;
}
public void run() {
Thread thisThread = Thread.currentThread();
while (myself == thisThread) {
<behavior here…>
//RECHECK CONDITION IF THE CODE IS LARGE
}
}
A Shared Counter
• ParallelCounter
– increment() increments the counter
– decrement() decrements the counter
• CountingThread
– Parameters:
• String name
• Int times
– Execution: increments or decrements the
counter for times times
What Happens?
Incrementor i1 = new CountingThread("A",5000000);
Incrementor i2 = new CountingThread("B",5000000);
i1.start();
i2.start();
while (!i1.hasFinished || !i2.hasFinished) {}
System.out.println(counter);
What is the final value of the counter
(when both i1 and i2 have finished)?
Counter
static void increment() {
counter++;
}
Byte code
cur counter
counter cur + 1
Counter
static void increment() {
counter++;
}
Byte code
cur counter
counter cur + 1
What if we have a context
switch here?
Remember:
counter is shared, cur is local
Counter
static void increment() {
int curVal = counter;
Thread.yield();
counter = curVal+1;
}
The Problem
• Threads share the same address space
• Common resources: objects can be manipulated
by multiple threads at the same time
• Interference we need synchronization
• Access control mechanisms: to define who and
when can correctly access to a resource
– No deadlock: situation in which every thread is stuck
– No livelock: situation in which threads continuously
execute without terminating
– No starvation: we must guarantee that all threads can
have the possibility of accessing the resource sooner
or later
Mutual Exclusion
• Operations on a shared objects by different
threads cannot overlap in time
• No ordering constraint
• Critical section: instructions that deal with the
object change
• Critical sections of the same class cannot
overlap
• At most one critical section of a class can be
executed at a given time
Semaphores (Dijkstra)
• Non-negative integers indicating number of
parallel accesses allowed
• manipulated through atomic operations:
signal (V) increment
wait (P) decrement
• Read semaphore: when value=0, wait causes
the thread to wait until the value becomes
positive
– Passive wait inside a queue
– FCFS strategy to avoid starvation
• Binary semaphores: mutex
Mutual Exclusion with Semaphores
Semaphore s = new Semaphore(1);
Thread 1
Thread 2
…
s.wait();
<critical section>
s.signal();
…
…
s.wait();
<critical section>
s.signal();
…
N.B.: THIS IS NOT JAVA CODE
• This kind of semaphore is a lock
Limits of Semaphores
• Low-level programming construct
• Easy to introduce errors: deadlock/livelock
– Debugging concurrent programs is extremely
difficult
• Examples in the mutex case
– Inverting wait and signal makes it possible to
multiple access to the critical section
– Using wait twice causes a deadlock
Monitor for Objects (Hoare)
Monitor (Abstract Model)
• Protects the data of a shared
structure/resource
– Data (state of the monitor) are persistent and
cannot be accessed directly
• Object’s data attributes
– Public/Entry methods: unique entry point that can
modify the state
Monitor Protection Levels
• Level 1: mutual exclusion
– Public methods are always mutually exclusive: at most
one thread can be active inside the monitor (lock)
– If another thread tries to access one of the public
methods waits into an entry queue
– Automatically enforced at the language level
• Level 2: regulates the order in which threads can
access the resource
– When an entry operation is called, it first checks a
synchronization condition (that induces an ordering)
– Condition not met thread waits, monitor is freed
– Suspension managed by the programmer through a
condition variable
Condition
• Special variable type
condition cond;
• Represents a queue containing waiting
threads
• Monitor’s operations manipulate conditions
through two operations
– wait(cond)
– signal(cond)
Signal and Wait
• Effect of wait(cond)
– Thread is suspended and inserted into cond’s
queue
– Monitor is freed
– The thread will restart the execution after
wait(cond), guaranteeing mutual exclusion
• Effect of signal(cond)
– Reactivates one thread waiting in cond’s queue
– Has no effect if cond’s queue is empty
Monitor Queues
Monitor
Condition queue (C1)
Entry queue
Condition queue (C2)
active thread
...
Condition queue (Cn)
Signal Semantics
• Execution of signal operation
– Signaling thread Q (executes signal(cond))
– Signaled thread P (extracted from cond’s queue)
• Both Q and P could execute… who has
priority?
– Signal and wait: Q halts and P continues
• Blocking condition variables
– Signal and continue: Q continues, P is notified and
waits
• Nonblocking condition variables
Signal Semantics: Schema
cond.signal_and_wait()
cond.signal_and_continue()
entry
queue
condition
queue
cond.wait()
monitor free
in execution
cond.signal_and_wait()
cond.signal_and_continue()
NO
Call
monitor
free?
YES
Signal and Wait
• Signaling thread: Q, signaled thread: P
• Q is inserted into the entry queue
– Or a second signal queue with higher
priority than the entry queue
• P is activated and occupies the monitor
– No other process can
change the condition
– Hence P can continue the
execution of the method
– P restarts after the
cond.wait()instruction
that blocked it
Signal and Continue (Java)
• Signaling thread: Q, signaled thread: P
• Q continues (monitor still locked)
• P is moved from cond’s queue
to the entry queue
– Other processes can enter the
monitor between Q and P
First instruction to be executed
by P when it will be selected:
re-test the synch. condition!
– Pattern:
while(!synch_cond) cond.wait();
<access to resource>
Example: Bounded Mailbox
• Shared mailbox with capacity N
• Monitor: circular message buffer (of size N)
• Two entry operations to be executed in isolation
– void send(Message m)
Cannot be accomplished if the buffer is full
– Message receive()
Cannot be accomplished if the buffer is empty
Producer
Thread
Consumer
Thread
monitor circular_buffer{
messagge buffer[N];
int occupied=0; int head=0; int bottom=0;
condition not_full;
condition not_empty;
public void receive(messagge m){ /*proc. entry -> mutually exclusive */
if (occupied==N) not_full.wait;
buffer[code]=m;
head=(head + 1)%N;
occupied++;
not_empty.signal;
}
public messagge send(){ /*proc. entry -> mutually exclusive */
messagge m;
if (occupied == 0) not_empty.wait;
m=buffer[head];
bottom=(bottom + 1)%N;
occupied--;
not_full.signal;
return m;
}
}
Synchronization in Java
• Global environment (shared data): threads
interact/interfere by operating on shared
objects
– Competition: synchronized methods, locks
– Cooperation: semaphores and wait-notify,
conditions
• Package java.util.concurrent
Basic Monitor in Java
• JVM associates a lock to every object
– States whether the object is free/occupied
– Entry set queue for suspended threads
• Critical sections can be identified with the
keyword synchronized
– Synchronized methods
– Synchronized sections in the code
Synchronized Section
• Compiler manages a synchronized section
– Adding a prologue for acquiring the object’s lock
• Lock free thread can execute the critical section
• Lock busy thread suspended into the entry set
queue of the object
– Adding an epilogue for releasing the lock
• No suspended thread lock released
• Else lock maintained and assigned to one of the
suspended threads
Synchronized Block
synchronized(this) { <code> }
• <code> is a critical section for object this
• Mutual exclusion is guaranteed for this w.r.t.
– Other executions of the same block
– Other synchronized blocks for the same object
<Object mutexLock = …>
…
public void M( ) {
<non-critical section>;
synchronized (this){<critical section>;}
<non-critical section>;
}
Thread-Safe Counter
static synchronized void increment() {
counter++;
}
Byte code
<lock acquisition or suspension>
cur counter
counter cur + 1
<lock release or reassignment>
• Synchronization is related to this (Counter object)
• Mutual exclusion ensured for all synchronized methods
– E.g. also dec() method for Counter
Wait Set
• In addition to the entry set, each object is
associated to another queue: wait set
• Special methods
– wait(): insertion in the wait set
– notify()/ notifyAll(): extraction from the
wait set
– Can be invoked only by the thread owning the
object’s lock
• Can be used only in a synchronized section
Wait and Notify(All)
• wait(): calling thread
– Releases the lock
– Is suspended in the wait set queue
– N.B.: method throws InterruptedException
• notify(): one thread is selected from the
wait set and moved into the entry set
• notifyAll(): all threads in the wait set are
moved into the entry set
Policy: signal and continue!
Example: Bounded Mailbox
• void send(Message m)
– Synchronized
– Cyclic test (mailbox full wait())
– After message insertion notify all
• Message receive()
– Synchronized
– Cyclic test (mailbox empty wait())
– After message extraction notify all
• Is this efficient?
Producer
Thread
Consumer
Thread
Limitations of the Basic Monitor
• One queue for mutual
exclusion (entry set)
• Only one queue for
synchronizing threads on an
object (wait set)
Conditions and Semaphors
(from Java 5)
Java Semaphores
• Java (from 5.0) also explicitly supports
semaphores
(java.util.concurrent.Semaphore)
• Constructor: takes
– Permits (no.): number of permits initially granted
– Fairness (yes/no): if true, suspended threads will
acquire the resource in the order they asked for it
• Basic methods
– acquire() wait
– release() signal
• Other useful methods (see JavaDoc)
Example: Bounded Mailbox
• How to realize it without a monitor, but only
semaphores?
• How many semaphores?
• How are they initialized?
Producer
Thread
Consumer
Thread
Other Useful Methods
• sleep(long ms)
– Suspends thread for the specified amount of time
• interrupt()
– Triggers an event that causes the interruption of thread
• interrupted() isInterrupted()
– Check whether the current thread has been interrupted
• join()
– Waits for the termination of the specified thread
• isAlive()
– true if thread has been started and has not yet terminated its
execution
• yield()
– Forces thread to release CPU
• Other useful methods: JavaDoc of Thread class
– E.g. Thread.getCurrentThread() returns the currently active
thread
On Deprecated Methods
• stop() forces thread termination
– All resources (e.g. locks) are instantaneously freed
– If the thread was doing an “atomic” operation, the object’s state
could be left in an inconsistent state
– The object can be now accessed by other thread
Should not be used
• suspend() blocks a thread until resume() is invoked
– Resources are not freed (e.g. locks are maintained)
– If the thread has acquired a mutually exclusive resource
(monitor), the resource is blocked
Should not be used
• In general, follow the “Java Thread Primitive Deprecation”
guide (part of Java SE Documentation)
Thread-safe Datastructures
• Common data structures not thread-safe
– e.g. LinkedList
• Synchronized data structures
– BlockingQueue
– ConcurrentMap
– CopyOnWriteArrayList
On Priorities
• Threads have two methods for priorities
– setPriority(int v)
v∈[MIN_PRIORITY, MAX_PRIORITY]
– getPriority()
• Java suggested best practice:
– Among runnable threads, choose the ones of higher priority
– Round robin over threads of the same priority
– Interrupt thread if a thread of higher priority is runnable
• Some JVM implementations: delegate threads’ scheduling
to O.S.
behavior depends on JVM + OS
Use with care
Take home
• Java threads:
– extend Thread
– implement Runnable
• Synchronization an issue
– Java provides techniques
• synchronized classes/methods/code snippets
• semaphores
• Synchronized data structures