Monday, January 25, 2016

Topic 30: MultiThreading in Java

  • Java provides built-in support for multithreaded programming. A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called a thread, and each thread defines a separate path of execution. Thus, multithreading is a specialized form of multitasking. 

  • Threads are lightweight. They share the same address space and cooperatively share the same heavyweight process. 

    Interthread communication is inexpensive, and context switching from one thread to the next is low cost.

  • Java uses threads to enable the entire environment to be asynchronous. This helps reduce inefficiency by preventing the wastage of CPU cycles.

  • Threads exist in several states. A thread can be running. It can be ready to run as soon as it gets CPU time. A running thread can be suspended, which temporarily suspends its activity. A suspended thread can then be resumed, allowing it to pick up where it left off. A thread can be blocked when waiting for a resource. At any time, a thread can be terminated, which halts its execution immediately. Once terminated, a thread cannot be resumed.

  • Thread priorities are integers that specify the relative priority of one thread to another. A thread’s priority is used to decide when to switch from one running thread to the next. This is called a context switch. The rules that determine when a context switch takes place are simple:

  • A thread can voluntarily relinquish control. In this scenario, all other threads are examined, and the highest-priority thread that is ready to run is given the CPU.

  • A thread can be preempted by a higher-priority thread. In this case, a lower-priority thread that does not yield the processor is simply preempted—no matter what it is doing— by a higher-priority thread. This is called preemptive multitasking.

For operating systems such as Windows, threads of equal priority are time-sliced automatically in round-robin fashion. For other types of operating systems, threads of equal priority must voluntarily yield control to their peers. If they don’t, the other threads will not run.

  • To synchronize two or more threads that share a resource, Java uses the monitor. We can think of a monitor as a very small box that can hold only one thread. Once a thread enters a monitor, all other threads must wait until that thread exits the monitor. In this way, a monitor can be used to protect a shared asset from being manipulated by more than one thread at a time. There is no class “Monitor”; instead, each object has its own implicit monitor that is automatically entered when one of the object’s synchronized methods is called. Once a thread is inside a synchronized method, no other thread can call any other synchronized method on the same object.

  • After we divide our program into separate threads, we need to define how they will communicate with each other. Java provides a clean, low-cost way for two or more threads to talk to each other, via calls to predefined methods that all objects have. Java’s messaging system allows a thread to enter a synchronized method on an object, and then wait there until some other thread explicitly notifies it to come out.

  • Java’s multithreading system is built upon the Thread class, its methods, and its companion interface, Runnable

    Thread encapsulates a thread of execution.  

    To create a new thread, our program will either extend Thread or implement the Runnable interface. 

  • When a Java program starts up, one thread begins running immediately. This is usually called the main thread. It is the one that is executed when program begins. The main thread is important for two reasons:

  • It is the thread from which other “child” threads will be spawned.

  • Often, it must be the last thread to finish execution because it performs various shutdown actions.

Main thread, like child threads, can be controlled through a Thread object. To do so, we must obtain a reference to it by calling the method currentThread( ), which is a public static member of Thread. Its general form is shown here:

static Thread currentThread( )

This method returns a reference to the thread in which it is called. For example:

// Controlling the main Thread.
class CurrentThreadDemo
{
public static void main(String args[])
{
Thread t = Thread.currentThread();
System.out.println("Current thread: " + t);


// change the name of the thread
t.setName("My Thread"); //setName() changes the internal name of the thread
System.out.println("After name change: " + t);
try
{
for(int n = 5; n > 0; n--)
{
Thread.sleep(1000); //argument is delay period in milliseconds
}
}
//The sleep( ) method in Thread might throw an InterruptedException. This would happen if some other thread wanted to interrupt this sleeping one.
catch (InterruptedException e)
{
System.out.println("Main thread interrupted");
}
}
}

Here is the output generated by this program:

Current thread: Thread[main,5,main]

After name change: Thread[My Thread,5,main]

Notice the output produced when t is used as an argument to println( ). This displays, in order: the name of the thread, its priority, and the name of its group. By default, the name of the main thread is main. Its priority is 5, which is the default value, and main is also the name of the group of threads to which this thread belongs. A thread group is a data structure that controls the state of a collection of threads as a whole. 

  • The sleep( ) method causes the thread from which it is called to suspend execution for the specified period of milliseconds. Its general form is shown here:

static void sleep(long milliseconds) throws InterruptedException

The sleep( ) method has a second form, shown next, which allows to specify the period in terms of milliseconds and nanoseconds:

static void sleep(long milliseconds, int nanoseconds) throws InterruptedException

This second form is useful only in environments that allow timing periods as short as nanoseconds.

  • We can set the name of a thread by using setName( ). We can obtain the name of a thread by calling getName( ) . These methods are members of the Thread class and are declared like this:
    final void setName(String threadName)
    final String getName( )

  • The easiest way to create a thread is to create a class that implements the Runnable interface. To implement Runnable, a class need only implement a single method called run( ), which is declared like this:

public void run( )

Inside run( ), we will define the code that constitutes the new thread. run( ) establishes the entry point for another, concurrent thread of execution within our program. This thread will end when run( ) returns.

After we create a class that implements Runnable, we will instantiate an object of type Thread from within that class. Thread defines several constructors. The one that we will use is shown here:

Thread(Runnable threadOb, String threadName)

In this constructor, threadOb is an instance of a class that implements the Runnable interface. This defines where execution of the thread will begin. The name of the new thread is specified by threadName. After the new thread is created, it will not start running until we call its start( ) method, which is declared within Thread. In essence, start( ) executes a call to run( ). The start( ) method is shown here:

void start( )

Here is an example that creates a new thread and starts it running:

// Create a second thread.
class NewThread implements Runnable
{
Thread t;

NewThread()
{
// Create a new, second thread
t = new Thread(this, "Demo Thread"); //constructor for Thread class

System.out.println("Child thread: " + t);
t.start(); // Start the thread
}

// This is the entry point for the second thread.
public void run()
{
try
{
for(int i = 5; i > 0; i--)
{
System.out.println("Child Thread: " + i);
Thread.sleep(500);
}
}
catch (InterruptedException e)
{
System.out.println("Child interrupted.");
}
System.out.println("Exiting child thread.");
}


}


class ThreadDemo
{
public static void main(String args[])
{
new NewThread(); // create a new thread
try
{
for(int i = 5; i > 0; i--)
{
System.out.println("Main Thread: " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread exiting.");
}
}

After calling start( ), NewThread’s constructor returns to main( ). Both threads continue running, sharing the CPU, until their loops finish.


As mentioned earlier, in a multithreaded program, often the main thread must be the last thread to finish running. The preceding program ensures that the main thread finishes last, because the main thread sleeps for 1,000 milliseconds between iterations, but the child thread sleeps for only 500 milliseconds.

  • The second way to create a thread is to create a new class that extends Thread, and then to create an instance of that class. The extending class must override the run( ) method, which is the entry point for the new thread. It must also call start( ) to begin execution of the new thread. Here is the preceding program rewritten to extend Thread:

// Create a second thread by extending Thread
class NewThread extends Thread
{
NewThread()
{
// Create a new, second thread
super("Demo Thread");

System.out.println("Child thread: " + this);
start(); // Start the thread
}
// This is the entry point for the second thread.
public void run()
{
try
{
for(int i = 5; i > 0; i--)
{
System.out.println("Child Thread: " + i);
Thread.sleep(500);
}
}
catch (InterruptedException e)
{
System.out.println("Child interrupted.");
}
System.out.println("Exiting child thread.");
}
}
class ExtendThread
{
public static void main(String args[])
{
new NewThread(); // create a new thread
try
{
for(int i = 5; i > 0; i--)
{
System.out.println("Main Thread: " + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread exiting.");
}
}

Notice the call to super( ) inside NewThread. This invokes the following form of the Thread constructor:

public Thread(String threadName)

Here, threadName specifies the name of the thread.

  • Often we will want the main thread to finish last. Two ways exist to determine whether a thread has finished. 

    First, we can call isAlive( ) on the thread. This method is defined by Thread, and its general form is shown here:

final boolean isAlive( )

The isAlive( ) method returns true if the thread upon which it is called is still running. It returns false otherwise.


While isAlive( ) is occasionally useful, we will more commonly use join( ) to wait for a thread to finish, shown here:

final void join( ) throws InterruptedException

This method waits until the thread on which it is called terminates.


Preceding example using Runnable interface and sleep() rewritten using join( ) to ensure that the main thread is the last to stop. It also demonstrates the isAlive( ) method.

NewThread ob1 = new NewThread();
System.out.println("Thread One is alive: " + ob1.t.isAlive());
// wait for threads to finish
try
{
System.out.println("Waiting for threads to finish.");
ob1.t.join();
}
catch (InterruptedException e)
{
System.out.println("Main thread Interrupted");
}

  • To set a thread’s priority, use the setPriority( ) method, which is a member of Thread. This is its general form:

final void setPriority(int level)

Here, level specifies the new priority setting for the calling thread. The value of level must be within the range MIN_PRIORITY and MAX_PRIORITY. Currently, these values are 1 and 10, respectively. To return a thread to default priority, specify NORM_PRIORITY, which is currently 5. These priorities are defined as static final variables within Thread.


We can obtain the current priority setting by calling the getPriority( ) method of Thread, shown here:

final int getPriority( )

The following example demonstrates two threads at different priorities. One thread is set two levels above the normal priority, as defined by Thread.NORM_ PRIORITY, and the other is set to two levels below it.

// Demonstrate thread priorities.
Thread t1 = new Thread(this);
Thread t2 = new Thread(this);

t1.setPriority(Thread.NORM_PRIORITY + 2);
t2.setPriority(Thread.NORM_PRIORITY - 2);

  • When two or more threads need access to a shared resource, they need some way to ensure that the resource will be used by only one thread at a time. The process by which this is achieved is called synchronization. Key to synchronization is the concept of the monitor (also called a semaphore).
    We can synchronize our code in either of two ways:

      Using Synchronized Methods

      Synchronization is easy in Java, because all objects have their own implicit monitor associated with them. To enter an object’s monitor, just call a method that has been modified with the synchronized keyword. While a thread is inside a synchronized method, all other threads that try to call it (or any other synchronized method) on the same instance have to wait. To exit the monitor and relinquish control of the object to the next waiting thread, the owner of the monitor simply returns from the synchronized method. To understand the need for synchronization, let’s begin with a simple example.

    // This program is not synchronized.
    class Callme
    {

    synchronized void call(String msg)
    {
    System.out.print("[" + msg);
    try
    {
    Thread.sleep(1000);
    }
    catch(InterruptedException e)
    {
    System.out.println("Interrupted");
    }
    System.out.println("]");
    }
    }
    class Caller implements Runnable
    {
    String msg;
    Callme target;
    Thread t;
    public Caller(Callme targ, String s)
    {
    target = targ;
    msg = s;
    t = new Thread(this);
    t.start();
    }
    public void run()
    {
    target.call(msg);
    }
    }
    class Synch
    {
    public static void main(String args[])
    {
    Callme target = new Callme();
    Caller ob1 = new Caller(target, "Hello");
    Caller ob2 = new Caller(target, "Synchronized");
    Caller ob3 = new Caller(target, "World");
    // wait for threads to end
    try
    {
    ob1.t.join();
    ob2.t.join();
    ob3.t.join();
    }
    catch(InterruptedException e)
    {
    System.out.println("Interrupted");
    }
    }
    }

    Here is the output produced by this program:

    [Hello] [Synchronized] [World]

    The synchronized Statement

    Suppose class was created by a third party, and we do not have access to the source code. Thus, we can’t add synchronized to the appropriate methods within the class. But we can simply put calls to the methods defined by this class inside a synchronized block. This is the general form of the synchronized statement:

    synchronized(object) {

    // statements to be synchronized

    }

    Here, object is a reference to the object being synchronized. A synchronized block ensures that a call to a method that is a member of object occurs only after the current thread has successfully entered object’s monitor. Here is an alternative version of the preceding example, using a synchronized block within the run( ) method:

    // synchronize calls to call()
    public void run()
    {
    synchronized(target)


    { // synchronized block
    target.call(msg);
    }

    }

    • Interthread communication using final methods in Object wait( ), notify( ), notifyAll( ) methods. All classes have them.

      final void wait( ) throws InterruptedException

      final void notify( )

      final void notifyAll( )

      All three methods can be called only from within a synchronized context.

    wait( ) tells the calling thread to give up the monitor and go to sleep until some other thread enters the same monitor and calls notify( ).

    notify( ) wakes up a thread that called wait( ) on the same object.

    notifyAll( ) wakes up all the threads that called wait( ) on the same object. One of the threads will be granted access.

    Sun recommends that calls to wait( ) should take place within a loop that checks the condition on which the thread is waiting. The following example shows this technique.

    • The following sample program that implements a simple form of the producer/ consumer problem. It consists of four classes: Q representing the queue, Producer, the threaded object that is producing queue entries; Consumer, the threaded object that is consuming queue entries; and PC, the tiny class that creates the single Q, Producer, and Consumer.

    class Q
    {
    int n;
    boolean emptyQ = false;

    synchronized int get()
    {
    while(!emptyQ)
    try
    {
    wait();
    }
    catch(InterruptedException e)
    {
    System.out.println("InterruptedException caught");
    }
    System.out.println("Got: " + n);
    emptyQ = false;
    notify();
    return n;
    }

    synchronized void put(int n)
    {
    while(emptyQ)
    try
    {
    wait();
    }
    catch(InterruptedException e)
    {
    System.out.println("InterruptedException caught");
    }
    this.n = n;
    emptyQ = true;
    System.out.println("Put: " + n);
    notify();
    }
    }

    class Producer implements Runnable
    {
    Q q;
    Producer(Q q)
    {
    this.q = q;
    new Thread(this, "Producer").start();
    }

    public void run()
    {
    int i = 0;
    while(true)
    {
    q.put(i++);
    }
    }
    }

    class Consumer implements Runnable
    {
    Q q;
    Consumer(Q q)
    {
    this.q = q;
    new Thread(this, "Consumer").start();
    }
    public void run()
    {
    while(true)
    {
    q.get();
    }
    }
    }

    class PC
    {
    public static void main(String args[])
    {
    Q q = new Q();
    new Producer(q);
    new Consumer(q);
    System.out.println("Press Control-C to stop.");
    }
    }

    • Deadlock occurs when two threads have a circular dependency on a pair of synchronized objects. For example, suppose one thread enters the monitor on object X and another thread enters the monitor on object Y. If the thread in X tries to call any synchronized method on Y, it will block as expected. However, if the thread in Y, in turn, tries to call any synchronized method on X, the thread waits forever, because to access X, it would have to release its own lock on Y so that the first thread could complete.



    Click for NEXT article.



    Please feel free to correct me by commenting your suggestions and feedback.

    No comments:

    Post a Comment