Thread

Advanced Programming

SUT • Spring 2019

Contents

  • Need for multi-thread programming

  • Threads in java

  • Samples

  • Synchronization

    • synchronized

    • wait & notify

    • join

Program Executation

Sequential Programming

  • 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 and Multithreading

  • 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

Concurrency vs. Parallelism

What are Threads Good For?

  • 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).

Parallel Processing

  • Multi-Processor Systems
  • Multi-core CPUs
    • Dual core
    • Core2duo
    • Corei7, corei5
  • Even with no multi-core processors, Multithreading is useful
    • How?
      • I/O bounded tasks
      • Responsive UI
      • Simulated multi-threading

OS Support

  • Multi-task OS

    • Windows & Unix

  • Multi-thread OS

  • Single task OS

    • DOS

Language Support

  • 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

Application Thread

  • 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

Multiple Threads in an Application

  • 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

Creating Threads

  • 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

Extending Thread

public class ThreadExample extends Thread {
    public void run() {
	for (int i = 1; i <= 100; i++) {
	    System.out.println("Thread: " + i);
	}
    }
}

Thread Methods

  • 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

Thread Methods

  • 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

Implementing Runnable

public class RunnableExample implements Runnable {
    public void run () {
        for (int i = 1; i <= 100; i++) {
            System.out.println ("Runnable: " + i);
        }
    }
}

A Runnable Object

  • The Thread object’s run() method calls the Runnable object’s run() method

  • Allows threads to run inside any object, regardless of inheritance

Starting the Threads

public class ThreadsStartExample {
    public static void main (String argv[]) {
        new ThreadExample().start();
        new Thread(new RunnableExample()).start();
    }
}

Scheduling Threads

More Thread States

  • getState() method in Thread

Thread State Diagram

Example

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();
	    }
	}
    }
}

Running the Threads

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();
    }
}

GUI Example

  • Start Counting = starts counting the counter
  • Stop Counting = stops counting the counter

UnresponsiveUI

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++;
         }
    }
});

UnresponsiveUI (2)

StopButton:

stopButton.addActionListener(
    new ActionListener() {
        public void actionPerformed(ActionEvent evt) { 
            stop = true; 
        }
    });

ResponsiveUI

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();
    }
});

Java Scheduling

  • 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

Thread Priority

  • 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

Thread Priority (2)

  • 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

Thread Priority (3)

  • 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

Some Notes

  • Thread implementation in Java is actually based on operating system support
  • Some Windows operating systems support only 7 priority levels, so different levels in Java may actually be mapped to the same operating system level

Daemon Threads

  • Daemon threads are “background” threads, that provide services to other threads, e.g., the garbage collection thread
  • The Java VM will not exit if non-Daemon threads are executing
  • The Java VM will exit if only Daemon threads are executing
  • Daemon threads die when the Java VM exits
  • A thread becomes a daemon with setDaemon() method

Concurrency

Concurrency

  • An object in a program can be changed by more than one thread
  • Q: Is the order of changes that were preformed on the object important?

Race Condition

  • A race condition – the outcome of a program is affected by the order in which the program's threads are allocated CPU time
  • Two threads are simultaneously modifying a single object
  • Both threads “race” to store their value

Monitors

  • 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

Monitor (cont.)

  • 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

Critical Section

  • The synchronized methods define critical sections

  • Execution of critical sections is mutually exclusive. Why?

Example

public class BankAccount {
    private float balance;

    public synchronized void deposit(float amount) {
        balance += amount;
    }
 
    public synchronized void withdraw(float amount) {
        balance -= amount;
    }        
}

Static Synchronized Methods

  • 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?

Synchronized Statements

  • 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); 
    } 
} 

Two Identical Methods

private synchronized void g() {
    h();
}

Private void g() {
    synchronized(this){
	h();
    }
}


Join()

  • A method can wait for finishing another thread

  • Using thread.join()

Wait and Notify

  • 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

  • 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

The wait() Method

  • 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

The wait() Method

  • 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

notify() and notifyAll()

  • 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.

wait() and the Lock

  • The object lock is released during the wait()

Some Notes

  • 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

Example: Producer/Consumer

  • 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.

Title Text

Made with Slides.com