Download Notes on Threads and Synchronization - Slides | CMSC 433 and more Study notes Programming Languages in PDF only on Docsity!
CMSC 433 – Programming Language
Technologies and Paradigms
Spring 2007
Threads and Synchronization
May 8, 2007
2
Computation Abstractions
CPU 1
CPU 2
p
p
p
p
t t
t t
t
t
t t
A computer
Threads(e.g., JVM’s)Processes
3
Processes vs. Threads
}…x…foo() { int x;
}…x…foo() {int x;
int (^) x;
foo() { …x…
}
foo()
(^) {
}…x…
share data Processes do not
within a processThreads share data
fork() in C
4
So, What Is a Thread?
Conceptually
: it is a parallel computation
occurring within a process
Implementation view
: it’s a program
area are shared among all threadscounter and a stack. The heap and static
• All programs have at least one thread
(main)
5
Why Multiple Threads?
• Performance:
- Concurrency of computation and I/O– Parallelism on multiprocessors
• Can easily express some programming
– Simulations– Event processingparadigms
• Keep computations separate, as in an OS
6
Why Not Multiple Threads?
• Overhead• Complexity
7
Programming Threads
• Threads are available in many languages
- C, C++, Objective Caml, Java, SmallTalk …
• In many languages (e.g., C and C++),
– Not part of the language specificationthreads are a platform specific add-on
• Part of the Java language specification
8
Java Threads
• Every application has at least one thread
- The “main” thread, started by the JVM to run
the application’s
main()
method.
• The code executed by
main()
can create
– Explicitly, using theother threads
Thread
class
- Implicitly, by calling libraries that create
- RMI, AWT/Swing, Applets, etc.threads as a consequence
13
Thread Scheduling
• Once a new thread is created, how does it
interact with existing threads?
• This is a question of scheduling:
- Given N processors and M threads, which
thread(s) should be run at any given time?
14
Thread Scheduling
• OS schedules a single-threaded process on a
single processor
• Multithreaded process scheduling:
- One thread per processor
- Exploits hardware-level concurrency• Effectively splits a process across CPU’s
- Many threads per processor
- Need to share CPU in slices of time
15
Scheduling Example (1)
CPU 2 CPU 1
p2 p1 p2 p
One process per CPU
p2 threads:
p1 threads:
16
Scheduling Example (2)
CPU 2 CPU 1
p2 p1 p2 p
Threads shared between CPU’s
p2 threads:
p1 threads:
17
Scheduling Consequences
• Concurrency
- Different threads from the same application can
be running
at the same time
on different
processors
• Interleaving
pre-empted
at any time
in
order to schedule other threads
18
Thread Scheduling
- What thread to run next– When the current thread should stop runningWhen multiple threads share a CPU, must decide:
A thread can voluntarily
yield()
the CPU
- Call to yield may be ignored; don’t depend on it
Preemptive schedulers
can de-schedule the current
- Not all JVMs use preemptive scheduling, so a threadthread at any time stuck in a loop may
(^) never
(^) yield by itself. Therefore,
put (^) yield()
(^) into loops
(e.g., on a lock or on I/O) or go to sleepThreads are de-scheduled whenever they block
19
Thread Lifecycle
• While a thread executes, it goes through a
– number of different phases
New
: created but not yet started
Runnable
: is running, or can run on a free CPU
Blocked
: waiting for I/O or on a lock
Sleeping
: paused for a user-specified interval
Terminated
: completed
20
Which Thread to Run Next?
- They finished sleeping, etc.– I/O became available– A lock was releasedincluding threads that were unblocked becauseThe scheduler looks at all of the runnable threads,
This can be set withOf these threads, it considers the thread’s priority.
setPriority().
Higher priority
- Oftentimes, threads waiting for I/O are also preferred.threads get preference.
25
Data Race Example
t1.run() { static int cnt = 0; cnt = y + 1;int y = cnt;
t2.run() {} cnt = y + 1;int y = cnt;
cnt = 0
global count.run. Each will increment the Start: both threads ready to Shared state
26
Data Race Example
t1.run() { static int cnt = 0; cnt = y + 1;int y = cnt;
t2.run() {} cnt = y + 1;int y = cnt;
cnt = 0
the global counter value into y. T1 executes, grabbing Shared state
y = 0
27
Data Race Example
t1.run() { static int cnt = 0; cnt = y + 1;int y = cnt;
t2.run() {} cnt = y + 1;int y = cnt;
cnt = 1
counter value T1 executes again, storing the Shared state
y = 0
28
Data Race Example
t1.run() { static int cnt = 0; cnt = y + 1;int y = cnt;
t2.run() {} cnt = y + 1;int y = cnt;
cnt = 1
counter value into y.grabbing the global T1 finishes. T2 executes, Shared state
y = 1 y = 0
29
Data Race Example
t1.run() { static int cnt = 0; cnt = y + 1;int y = cnt;
t2.run() {} cnt = y + 1;int y = cnt;
cnt = 2
incremented cnt value. T2 executes, storing the Shared state
y = 1 y = 0
30
But When I Run it Again?
31
Data Race Example
t1.run() { static int cnt = 0; cnt = y + 1;int y = cnt;
t2.run() {} cnt = y + 1;int y = cnt;
cnt = 0
global count.run. Each will increment the Start: both threads ready to Shared state
32
Data Race Example
t1.run() { static int cnt = 0; cnt = y + 1;int y = cnt;
t2.run() {} cnt = y + 1;int y = cnt;
cnt = 0
the global counter value into y. T1 executes, grabbing Shared state
y = 0
37
Question
• If instead of
cnt = y+1; int y = cnt;
• We had written
cnt++;
• Answer: NO! • Would the result be any different?
- Don’t depend on your intuition about atomicity
38
Question
• If you run a program with a race condition,
– ...and on the other threads/processes/etc that are– ...i.e., which JVM you’re running– No! It depends on the schedulerwill you always get an unexpected result?
running on the same CPU
• Race conditions are hard to find
39
Avoiding Interference:
Synchronization
public class Example extends Thread { public void run() {static Object lock = new Object();private static int cnt = 0; synchronized (lock)
cnt = y + 1;int y = cnt;
Lock
, for protecting
Acquires the shared state
(^) the lock;
Releases threadheld by anotherOnly succeeds if not
(^) the lock
40
Applying Synchronization
int (^) cnt =
(^) 0;
t1.run() { synchronized(lock) { int y
(^) = (^) cnt;
cnt =
(^) y (^) + 1;
}
t2.run() {} synchronized(lock) { int y
(^) = (^) cnt;
cnt =
(^) y (^) + 1;
}
}
cnt = 0
T1 acquires the lockShared state
41
Applying Synchronization
t1.run() { int cnt = 0; synchronized(lock) { cnt = y + 1;int y = cnt;
}
t2.run() {} synchronized(lock) { cnt = y + 1;int y = cnt;
}
}
cnt = 0
T1 reads cnt into yShared state
y = 0
42
Applying Synchronization
int (^) cnt =
(^) 0;
t1.run() { synchronized(lock) { int y
(^) = (^) cnt;
cnt =
(^) y (^) + 1;
}
t2.run() {} synchronized(lock) { int y
(^) = (^) cnt;
cnt =
(^) y (^) + 1;
}
}
cnt = 0
T1, so it blocksbecause it’s held byacquire the lock but failsT2 attempts toT1 is pre-empted.Shared state
y = 0
43
Applying Synchronization
t1.run() { int cnt = 0; synchronized(lock) { cnt = y + 1;int y = cnt;
}
t2.run() {} synchronized(lock) { cnt = y + 1;int y = cnt;
}
}
cnt = 1
to cntT1 runs, assigningShared state
y = 0
44
Applying Synchronization
int (^) cnt =
(^) 0;
t1.run() { synchronized(lock) { int y
(^) = (^) cnt;
cnt =
(^) y (^) + 1;
}
t2.run() {} synchronized(lock) { int y
(^) = (^) cnt;
cnt =
(^) y (^) + 1;
}
}
cnt = 1
and terminatesT1 releases the lockShared state
y = 0
49
Synchronized Statement
synchronized (obj) {
statements
• Obtains the lock on
obj
before executing
statements in block
• Releases the lock when the statement block
– Either normally, or due to a return, break, orcompletes
exception being thrown in the block
50
Synchronization not a Panacea
• Two threads can block on locks held by the
other; this is called
deadlock
Object
(^) A = (^) new Object();
Object
(^) B = (^) new Object();
T1.run() { synchronized (A)
(^) {
synchronized
(^) (B) {
…
}
}
}
T2.run() { synchronized (B)
(^) {
synchronized
(^) (A) {
…
}
}
}
51
Deadlock
• Quite possible to create code that deadlocks
A
B
- Thread 1 is trying to acquire a lock on
B
- Thread 2 is trying to acquire a lock on
A
• Not easy to detect when deadlock has
– Other than by the fact that nothing is happeningoccurred
52
Deadlock: Wait graphs
A
T
Thread T1 holds lock A
B
T
acquire lock BThread T2 attempting to
Deadlock occurs when there is a cycle in the graph
53
Wait graph example
A
B T
T
T1 holds lock on
A
T2 holds lock on
B
T1 is trying to acquire a lock on
B
T2 is trying to acquire a lock on
A
54
Guidelines for Programming with
Threads
Synchronize access to shared data
- Could cause deadlockDon’t hold multiple locks at a time
- Reduces blocking waiting for locksHold a lock for as little time as possible
- E.g., a method provided by someone else, especially ifdon’t understandWhile holding a lock, don’t call a method you you can’t be sure what it locks
- Corollary: document which locks a method acquires