Chapter 6 - Java - Multithreading Concurrency
Chapter 6 - Java - Multithreading Concurrency
Multithreading /
Concurrency
References
• Liang. Introduction to Java Programming, Seventh Edition,
2009 Pearson Education
3
6.1 Threads in Java
4
Thread
• Thread: single sequential flow of control within a
program
• Single-threaded program can handle one task at any
time.
• Multitasking allows single processor to run several
concurrent threads.
• Most modern operating systems support
multitasking.
5
Advantages of Multithreading (1)
• Reactive systems – constantly monitoring
• More responsive to user input – GUI application
can interrupt a time-consuming task
• Server can handle multiple clients simultaneously
• Can take advantage of parallel processing
6
Advantages of Multithreading (2)
• Different processes do not share memory space.
• A thread can execute concurrently with other
threads within a single process.
• All threads managed by the JVM share memory
space and can communicate with each other.
7
Threads Concept
Multiple
Thread 1
threads on
Thread 2
multiple
Thread 3
CPUs
Multiple
Thread 1
threads
Thread 2
sharing a
Thread 3
single CPU
8
Threads in Java
Creating threads in Java:
9
Threads in Java
Creating threads in Java:
• Extend java.lang.Thread class
• run() method must be overridden (similar to main
method of sequential program)
• run() is called when execution of the thread begins
• A thread terminates when run() returns
• start() method invokes run()
• Calling run() does not create a new thread
• Implement java.lang.Runnable interface
10
Threads in Java
Creating threads in Java:
• Extend java.lang.Thread class
• Implement java.lang.Runnable interface
• If already inheriting another class (i.e., JApplet)
• Single method: public void run()
• Thread class implements Runnable.
11
Thread States
12
Thread termination
A thread becomes Not Runnable when one of these
events occurs:
13
Creating Tasks and Threads
14
Exercise
15
The Thread Class
«interface»
java.lang.Runnable
java.lang.Thread
+Thread() Creates a default thread.
+Thread(task: Runnable) Creates a thread for a specified task.
+start(): void Starts the thread that causes the run() method to be invoked by the JVM.
+isAlive(): boolean Tests whether the thread is currently running.
+setPriority(p: int): void Sets priority p (ranging from 1 to 10) for this thread.
+join(): void Waits for this thread to finish.
+sleep(millis: long): void Puts the runnable object to sleep for a specified time in milliseconds.
+yield(): void Causes this thread to temporarily pause and allow other threads to execute.
+interrupt(): void Interrupts this thread.
16
The Static yield() Method
You can use the yield() method to temporarily release time
for other threads. For example, suppose you modify the
code in Lines 53-57 in TaskThreadDemo.java as follows:
public void run() {
for (int i = 1; i <= lastNum; i++) {
System.out.print(" " + i);
Thread.yield();
}
}
17
The static sleep Method
The sleep(long mills) method puts the thread to sleep for the specified
time in milliseconds. For example, suppose you modify the code in
Lines 53-57 in TaskThreadDemo.java as follows:
public void run() {
for (int i = 1; i <= lastNum; i++) {
System.out.print(" " + i);
try {
if (i >= 50) Thread.sleep(1);
}
catch (InterruptedException ex) {
}
}
}
Every time a number (>= 50) is printed, the print100 thread is put to
sleep for 1 millisecond.
18
The join() Method
You can use the join() method to force one thread to wait for another
thread to finish. For example, suppose you modify the code in Lines
53-57 in TaskThreadDemo.java as follows:
19
Thread States
20
Thread methods
isAlive()
• method used to find out the state of a thread.
• returns true: thread is in the Ready, Blocked, or Running state
• returns false: thread is new and has not started or if it is
finished.
interrupt()
f a thread is currently in the Ready or Running state, its
interrupted flag is set; if a thread is currently blocked, it is
awakened and enters the Ready state, and an
java.io.InterruptedException is thrown.
The isInterrupt() method tests whether the thread is interrupted.
21
The deprecated Methods
NOTE: The Thread class also contains the stop(), suspend(), and
resume() methods. As of Java 2, these methods are deprecated (or
outdated) because they are known to be inherently unsafe. You
should assign null to a Thread variable to indicate that it is stopped
rather than use the stop() method.
22
Thread Priority
• Each thread is assigned a default priority of
Thread.NORM_PRIORITY (constant of 5). You
can reset the priority using setPriority(int
priority).
23
Thread Scheduling
• An operating system’s thread scheduler determines
which thread runs next.
• Most operating systems use timeslicing for threads of
equal priority.
• Preemptive scheduling: when a thread of higher
priority enters the running state, it preempts the
current thread.
• Starvation: Higher-priority threads can postpone
(possible forever) the execution of lower-priority
threads.
24
6.2 Thread Management
25
Thread Management: Thread Pools
• Starting a new thread for each task could limit throughput and cause poor
performance.
• A thread pool is ideal to manage the number of tasks executing concurrently.
• Executor interface for executing Runnable objects in a thread pool
•ExecutorService is a subinterface of Executor.
«interface»
java.util.concurrent.Executor
+execute(Runnable object): void Executes the runnable task.
«interface»
java.util.concurrent.ExecutorService
+shutdown(): void Shuts down the executor, but allows the tasks in the executor to
complete. Once shutdown, it cannot accept new tasks.
+shutdownNow(): List<Runnable> Shuts down the executor immediately even though there are
unfinished threads in the pool. Returns a list of unfinished
tasks.
+isShutdown(): boolean Returns true if the executor has been shutdown.
+isTerminated(): boolean Returns true if all tasks in the pool are terminated.
26
Creating Executors
To create an Executor object, use the static methods in the Executors
class.
java.util.concurrent.Executors
+newFixedThreadPool(numberOfThreads: Creates a thread pool with a fixed number of threads executing
int): ExecutorService concurrently. A thread may be reused to execute another task
after its current task is finished.
+newCachedThreadPool(): Creates a thread pool that creates new threads as needed, but
ExecutorService will reuse previously constructed threads when they are
available.
27
6.3 Thread Synchronization
28
Thread Synchronization
1 0 newBalance = bank.getBalance() + 1;
2 0 newBalance = bank.getBalance() + 1;
3 1 bank.setBalance(newBalance);
4 1 bank.setBalance(newBalance);
29
Example: Showing Resource Conflict
• Objective: Write a program that demonstrates the problem of
resource conflict. Suppose that you create and launch one hundred
threads, each of which adds a penny to an account. Assume that
the account is initially empty.
java.lang.Runnable
100 1 1 1
AddAPennyTask AccountWithoutSync Account
-bank: Account -balance: int
+run(): void -thread: Thread[]
+getBalance(): int
+deposit(amount: int): void
+main(args: String[]): void
30
Race Condition
What, then, caused the error in the example? Here is a possible scenario:
1 0 newBalance = balance + 1;
2 0 newBalance = balance + 1;
3 1 balance = newBalance;
4 1 balance = newBalance;
);
• Effect: Task 1 did nothing (in Step 4 Task 2 overrides the result)
• Problem: Task 1 and Task 2 are accessing a common resource in a way that causes
conflict.
• Known as a race condition in multithreaded programs.
•A thread-safe class does not cause a race condition in the presence of multiple threads.
•The Account class is not thread-safe.
31
The ‘synchronized’ keyword
•Problem: race conditions
•Solution: give exclusive access to one thread at a time to code that
manipulates a shared object.
•Synchronization keeps other threads waiting until the object is
available.
•The synchronized keyword synchronizes the method so that only one
thread can access the method at a time.
•The critical region in the Listing 29.7 is the entire deposit method.
•One way to correct the problem in Listing 29.7: make Account
thread-safe by adding the synchronized keyword in deposit:
public synchronized void deposit(double amount)
32
Synchronizing Instance Methods
and Static Methods
•A synchronized method acquires a lock before it executes.
•Instance method: the lock is on the object for which it was
invoked.
•Static method: the lock is on the class.
•If one thread invokes a synchronized instance method
(respectively, static method) on an object, the lock of that
object (respectively, class) is acquired, then the method is
executed, and finally the lock is released.
•Another thread invoking the same method of that object
(respectively, class) is blocked until the lock is released.
33
Synchronizing Instance Methods
and Static Methods
With the deposit method synchronized, the preceding scenario cannot
happen. If Task 2 starts to enter the method, and Task 1 is already in
the method, Task 2 is blocked until Task 1 finishes the method.
Task 1 Task 2
34
Synchronizing Statements
•Invoking a synchronized instance method of an object acquires a
lock on the object.
•Invoking a synchronized static method of a class acquires a lock on
the class.
•A synchronized block can be used to acquire a lock on any object, not
just this object, when executing a block of code.
synchronized (expr) {
statements;
}
35
Synchronizing Statements vs. Methods
Any synchronized instance method can be converted into a
synchronized statement. Suppose that the following is a synchronized
instance method:
public synchronized void xMethod() {
// method body
}
36
Synchronization Using Locks
•A synchronized instance method implicitly acquires a lock on the
instance before it executes the method.
•You can use locks explicitly to obtain more control for coordinating
threads.
•A lock is an instance of the Lock interface, which declares the
methods for acquiring and releasing locks.
•newCondition() method creates Condition objects, which can be used
for thread communication.
«interface»
java.util.concurrent.locks.Lock
+lock(): void Acquires the lock.
+unlock(): void Releases the lock.
+newCondition(): Condition Returns a new Condition instance that is bound to this
Lock instance.
java.util.concurrent.locks.ReentrantLock
+ReentrantLock() Same as ReentrantLock(false).
+ReentrantLock(fair: boolean) Creates a lock with the given fairness policy. When the
fairness is true, the longest-waiting thread will get the
lock. Otherwise, there is no particular access order.
37
Fairness Policy
•ReentrantLock:concrete implementation of Lock for
creating mutually exclusive locks.
•Create a lock with the specified fairness policy.
•True fairness policies guarantee the longest-wait thread to
obtain the lock first.
•False fairness policies grant a lock to a waiting thread
without any access order.
•Programs using fair locks accessed by many threads may
have poor overall performance than those using the default
setting, but have smaller variances in times to obtain locks
and guarantee lack of starvation.
38
Cooperation Among Threads
•Conditions can be used for communication among threads.
•A thread can specify what to do under a certain condition.
•newCondition() method of Lock object.
•Condition methods:
•await() current thread waits until the condition is signaled
•signal() wakes up a waiting thread
•signalAll() wakes all waiting threads
«interface»
java.util.concurrent.Condition
+await(): void Causes the current thread to wait until the condition is signaled.
+signal(): void Wakes up one waiting thread.
+signalAll(): Condition Wakes up all waiting threads.
39
Cooperation Among Threads
•Lock with a condition to synchronize operations: newDeposit
•If the balance is less than the amount to be withdrawn, the withdraw
task will wait for the newDeposit condition.
•When the deposit task adds money to the account, the task signals
the waiting withdraw task to try again.
lock.lock(); lock.lock();
newDeposit.signalAll();
balance -= withdrawAmount
lock.unlock();
lock.unlock();
40
Example: Thread Cooperation
Write a program that demonstrates thread cooperation. Suppose that
you create and launch two threads, one deposits to an account, and
the other withdraws from the same account. The second thread has to
wait if the amount to be withdrawn is more than the current balance
in the account. Whenever new fund is deposited to the account, the
first thread notifies the second thread to resume. If the amount is still
not enough for a withdrawal, the second thread has to continue to
wait for more fund in the account. Assume the initial balance is 0 and
the amount to deposit and to withdraw is randomly generated.
41
Monitors
•Locks and conditions are more powerful and flexible than the
built-in monitor.
•A monitor is an object with mutual exclusion and
synchronization capabilities.
•Only one thread can execute a method at a time in the
monitor.
•A thread enters the monitor by acquiring a lock
(synchronized keyword on method / block) on the monitor
and exits by releasing the lock.
•A thread can wait in a monitor if the condition is not right for
it to continue executing in the monitor.
•Any object can be a monitor. An object becomes a monitor
once a thread locks it.
42
wait(), notify(), and notifyAll()
Use the wait(), notify(), and notifyAll() methods to facilitate
communication among threads.
The wait() method lets the thread wait until some condition occurs.
When it occurs, you can use the notify() or notifyAll() methods to
notify the waiting threads to resume normal execution. The
notifyAll() method wakes up all waiting threads, while notify() picks
up only one thread from a waiting queue.
43
Example: Using Monitor
Task 1 Task 2
synchronized (anObject) {
try { synchronized (anObject) {
// Wait for the condition to become true // When condition becomes true
while (!condition) resume anObject.notify(); or anObject.notifyAll();
anObject.wait(); ...
}
// Do something when condition is true
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
44
Case Study: Producer/Consumer
Consider the classic Consumer/Producer example. Suppose you use a buffer to
store integers. The buffer size is limited. The buffer provides the method write(int)
to add an int value to the buffer and the method read() to read and delete an int
value from the buffer. To synchronize the operations, use a lock with two
conditions: notEmpty (i.e., buffer is not empty) and notFull (i.e., buffer is not full).
When a task adds an int to the buffer, if the buffer is full, the task will wait for the
notFull condition. When a task deletes an int from the buffer, if the buffer is empty,
the task will wait for the notEmpty condition. The interaction between the two
tasks is shown in Figure 29.19.
notEmpty.signal(); notFull.signal();
45
Semaphores
Semaphores can be used to restrict the number of threads that access
a shared resource. Before accessing the resource, a thread must
acquire a permit from the semaphore. After finishing with the
resource, the thread must return the permit back to the semaphore.
46
Creating Semaphores
To create a semaphore, you have to specify the number of
permits with an optional fairness policy, as shown in Figure
29.29. A task acquires a permit by invoking the semaphore’s
acquire() method and releases the permit by invoking the
semaphore’s release() method. Once a permit is acquired, the
total number of available permits in a semaphore is reduced by
1. Once a permit is released, the total number of available
permits in a semaphore is increased by 1.
java.util.concurrent.Semaphore
+Semaphore(numberOfPermits: int) Creates a semaphore with the specified number of permits. The
fairness policy is false.
+Semaphore(numberOfPermits: int, fair: Creates a semaphore with the specified number of permits and
boolean) the fairness policy.
+acquire(): void Acquires a permit from this semaphore. If no permit is
available, the thread is blocked until one is available.
+release(): void Releases a permit back to the semaphore.
47
Deadlock
•Sometimes two or more threads need to acquire the locks on several shared
objects.
•This could cause deadlock, in which each thread has the lock on one of the objects
and is waiting for the lock on the other object.
•In the figure below, the two threads wait for each other to release the in order to
get a lock, and neither can continue to run.
1 synchronized (object1) {
2 synchronized (object2) {
3 // do something here
4 // do something here
5 synchronized (object2) {
6 synchronized (object1) {
// do something here // do something here
} }
} }
48
Thank you
for your
attentions!
49