Advanced Programming
SUT • Spring 2019
Need for multi-thread programming
Threads in java
Samples
Synchronization
synchronized
wait & notify
join
Up to this point, we learned sequential programming.
Everything in a program happens one step at a time.
What is wrong with this approach?
Multitasking refers to a computer's ability to perform multiple jobs concurrently
more than one program are running concurrently, e.g., UNIX
A thread is a single sequence of execution within a program
Multithreading refers to multiple threads of control within a single program
each program can run multiple threads of control within it, e.g., Web Browser
To maintain responsiveness of an application during a long running task.
To enable cancellation of separable tasks.
Some problems are intrinsically parallel.
To monitor status of some resource (DB).
Multi-task OS
Windows & Unix
Multi-thread OS
Single task OS
DOS
Some languages have no built-in mechanism for muli-threading
C, C++, …
QT as a solution
OS-dependent libraries
pthread in linux
Windows API
Java has multi-threading in its core language
Pros and cons
ISA experience
When we execute an application:
The JVM creates a Thread object whose task is defined by the main() method
It starts the thread
The thread executes the statements of the program one by one until the method returns and the thread dies
Each thread has its private run-time stack
If two threads execute the same method, each will have its own copy of the local variables the methods uses
However, all threads see the same dynamic memory (heap)
Two different threads can act on the same object and same static fields concurrently
There are two ways to create our own Thread object
Subclassing the Thread class and instantiating a new object of that class
Implementing the Runnable interface
In both cases the run() method should be implemented
public class ThreadExample extends Thread {
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("Thread: " + i);
}
}
}
void start()
Creates a new thread and makes it runnable
This method can be called only once
void run()
The new thread begins its life inside this method
sleep(int m)/sleep(int m,int n)
The thread sleeps for m milliseconds, plus n nanoseconds
yield()
Causes the currently executing thread object to temporarily pause and allow other threads to execute
Allow only threads of the same priority to run
Nothing is guaranteed for this method
public class RunnableExample implements Runnable {
public void run () {
for (int i = 1; i <= 100; i++) {
System.out.println ("Runnable: " + i);
}
}
}
The Thread object’s run() method calls the Runnable object’s run() method
Allows threads to run inside any object, regardless of inheritance
public class ThreadsStartExample {
public static void main (String argv[]) {
new ThreadExample().start();
new Thread(new RunnableExample()).start();
}
}
getState() method in Thread
class ThreadExample extends Thread {
public void run() {
MultiThreading.task("Thread");
}
}
class RunnableExample implements Runnable{
public void run() {
MultiThreading.task("Runnable");
}
}
public class MultiThreading {
public static void task(String taskName){
for(int i = 1; i <= 10; i++) {
System.out.println(taskName + ": " + i);
try {
Thread.sleep(new Random().nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args){
ThreadExample thr1 = new ThreadExample();
thr1.start();
RunnableExample run1 = new RunnableExample();
new Thread(run1).start();
ThreadExample thr2 = new ThreadExample();
thr2.start();
RunnableExample run2 = new RunnableExample();
new Thread(run2).start();
}
}
startButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent evt) {
stop = false;
for (int i = 0; i < 100000; i++) {
if (stop)
break;
tfCount.setText("" + countValue);
countValue++;
}
}
});
StopButton:
stopButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent evt) {
stop = true;
}
});
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
stop = false;
Thread t = new Thread() {
public void run() {
for (int i = 0; i < 100000; i++) {
if (stop)
break;
tfCount.setText("" + countValue);
countValue++;
try {
sleep(10);
} catch (InterruptedException ex) {}
}
}
};
t.start();
}
});
Thread scheduling is the mechanism used to determine how runnable threads are allocated CPU time
Scheduler is based on priority of threads
The priority of a thread: the importance of a thread to the scheduler
Uses fixed-priority scheduling:
Threads are scheduled according to their priority
Priority is compared with other threads in the ready queue
The scheduler will lean toward running the waiting thread with the highest priority first
Lower-priority threads just tend to run less often
The exact behavior depends on the platform
Usually, all threads should run at the default priority
Trying to manipulate thread priorities is usually a mistake
Every thread has a priority
When a thread is created, it inherits the priority of the thread that created it
The priority values range from 1 to 10, in increasing priority
The priority can be adjusted subsequently using the setPriority() method
The priority of a thread may be obtained using getPriority()
Priority constants are defined:
MIN_PRIORITY=1
MAX_PRIORITY=10
NORM_PRIORITY=5
Each object has a “monitor” that is a token used to determine which application thread has control of a particular object instance
In execution of a synchronized method (or block), access to the object monitor must be gained before the execution
Access to the object monitor is queued
Entering a monitor is also referred to as locking the monitor, or acquiring ownership of the monitor
If a thread A tries to acquire ownership of a monitor and a different thread has already entered the monitor, the current thread (A) must wait until the other thread leaves the monitor
The synchronized methods define critical sections
Execution of critical sections is mutually exclusive. Why?
public class BankAccount {
private float balance;
public synchronized void deposit(float amount) {
balance += amount;
}
public synchronized void withdraw(float amount) {
balance -= amount;
}
}
Marking a static method as synchronized, associates a monitor with the class itself
The execution of synchronized static methods of the same class is mutually exclusive.
Why?
A monitor can be assigned to a block
It can be used to monitor access to a data element that is not an object, e.g., array
byte[] array;
void arrayShift(int count) {
synchronized(array) {
System.arraycopy (array, count,array, 0, array.size - count);
}
}
private synchronized void g() {
h();
}
Private void g() {
synchronized(this){
h();
}
}
A method can wait for finishing another thread
Using thread.join()
Allows two threads to cooperate
Based on a single shared lock object
Marge put a cookie notify Homer and wait
Homer eat a cookie notify Marge and wait
The wait() method is part of the java.lang.Object interface
It requires a lock on the object’s monitor to execute
It must be called from a synchronized method, or from a synchronized segment of code.
In other words: The current thread should have acquired a lock on this object before calling any of the wait methods
Otherwise: IllegalMonitorStateException
wait() causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object
Upon call for wait(), the thread releases ownership of this monitor and waits until another thread notifies the waiting threads of the object
wait() is also similar to yield()
Both take the current thread off the execution stack and force it to be rescheduled
However, wait() is not automatically put back into the scheduler queue
notify() must be called in order to get a thread back into the scheduler’s queue
The need to the clock
Similar to wait() method:
The current thread should have acquired a lock on this object before calling notify() or notifyAll()
Otherwise: IllegalMonitorStateException
the task calling wait(), notify(), or notifyAll() must "own" (acquire) the lock for the object before it can call any of those methods.
The object lock is released during the wait()
Thread-safe Classes
E.g, StringBuffer is thread-safe
public synchronized StringBuffer append(String str){…}
and StringBuilder is NOT thread-safe
public StringBuilder append(String str) {…}
Threading Problems
Starvation
Deadlock
The producer–consumer problem
is a classic example of a multi-process synchronization problem
The problem:
two processes, the producer and the consumer,
who share a common, fixed-size buffer used as a queue.