Download Distributed Systems2. Java Threads

Document related concepts

Library (computing) wikipedia , lookup

Burroughs MCP wikipedia , lookup

Process management (computing) wikipedia , lookup

Thread (computing) wikipedia , lookup

Transcript
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