Multi-Threading in JAVA

INTRODUCTION

A program under execution is called a process, Process is complete, acquires a set of resources to run until it completes, and has its own memory area. In a system that has merely a single execution core, then only one process can execute at a time. Yet nowadays, more than one execution core is in the system. But in the case of a single core only one process can run at a time and it acquires all the required resources.

Multitasking can be of two types: process-based and, thread based. This can be done in two conditions: one is that a computer has more than one core processing unit (called multiprocessor), and the second is timesharing, assigning a fixed time slice to each process.

The second approach to multitasking is thread-based. But before we go in-depth with this approach, first understand what the thread is. A thread is the smallest unit of the single program, which is dedicated to a specific job. Thread is a lightweight process. Few resources are required to create a thread rather than a process. Each thread has a separate path of execution. Multitasking with a thread-based model provides an environment in which a single program (application) can carry out more than one task simultaneously.

Multitasking is implemented by Multi-threading, or we can say that multithreading is an extended version of multitasking using threads. Thus, a specialized form of multitasking is called multithreading. Multi-threading requires less overhead rather than a multitasking process. It reduces the overheads of inter-process communication because threads are lightweight processes. Context switching between the threads is also less costly. Because threads share the same address space of the process it belongs. Multi-threading facilitates writing an efficient program that makes optimum utilization of resources including CPU. 

THE JAVA THREAD MODEL

The Java language and its run-time system were designed keeping in mind about multithreading. Multithreading is best in all cases in contrast with the single-thread model. The single-thread system uses an approach of event loop with polling. According to this approach, a single thread in the system runs in an infinite loop. Polling is the mechanism, that selects a single event from the event queue to choose what to do next. As the event is selected, the event loop forwards the control to the corresponding required event handler. Nothing else can happen until the event handler returns. Because of this CPU time is wasted. Here, only one part of the complete program dominates the whole system, preventing the system from executing or starting any other process. In a single-thread model, one thread blocks all other threads until its execution completes. On the other waiting or idle thread can start and acquire the resource which is not in use by the current thread. This causes the wastage of resources. 

Java's multithreading provides benefits in this area by eliminating the loop and polling mechanism. One thread can be paused without stopping the other parts of the program. If any thread is paused or blocked, still other threads continue to run. 

As the process has several states, similarly a thread exists in several states. A thread can be in the following states:

Ready to run (New): First time as soon as it gets CPU time.

Running: Under execution.

Suspended: Temporarily not active or under execution.

Blocked: Waiting for resources.

Resumed: Suspended thread resumed, and started from where it left off.

Terminated: Halts the execution immediately and never resumes.

Java thread model can be defined in the following three sections:

  • Thread Priorities

Each thread has its own priority in Java. Thread priority is an absolute integer value. Thread priority decides only when a thread switches from one running thread to the next, called context switching. Priority does increase the running time of the thread or gives faster execution. 

  • Synchronization

Java supports asynchronous multithreading, any number of threads can run simultaneously without disturbing others to access individual resources at different instants of time or shareable resources. But sometimes it may be possible that shareable resources are used by at least two threads or more than two threads, one has to write at the same time, or one has to write and the other thread is in the middle of reading it. For such types of situations and circumstances, Java implements a synchronization model called monitor. The monitor was first defined by C.A.R. Hoare. You can consider the monitor as a box, in which only one thread can reside. As a thread enters in monitor, all other threads have to wait until that thread exits from the monitor. In such a way, a monitor protects the shareable resources used by it being manipulated by other waiting threads at the same instant of time. Java provides a simple methodology to implement synchronization.

  • Messaging

A program is a collection of more than one thread. Threads can communicate with each other. Java supports messaging between the threads with lost cost. It provides methods to all objects for inter-thread communication. As a thread exits from the synchronization state, it notifies all the waiting threads.

 UNDERSTANDING THREADS

 In Java multithreading is built upon two important entities given by the language: one is Thread class with its method, and the second is the Runnable interface. The Runnable interface defines only a single method run(), which contains the code executed by the thread in the classes it will be implemented. A Runnable object is passed to the Thread constructor. Another entity is the Thread class, which is the subclass of the Runnable interface. It implements Runnable, though its run() method does nothing. As such you can not refer to the insubstantial state of any running thread, you have to deal with the proxy of it. Thread encapsulates the thread of execution so that you must have an instance of Thread. For this, either extend the Thread class or implement the Runnable interface. The Thread class defines several purpose methods, a few of which are discussed below:

THE MAIN THREAD

As any Java program comes under execution, the first thread starts immediately, called the main thread. There are two important's in the main thread as follows:

  • Ø  It is the parent of all the threads of the program and all other "child" threads will be spawned from it.
  • Ø  It must be the last thread to finish the execution of the program.

As your program starts, the main thread is created automatically, under the control of the Thread object. But, if you want the reference of the main thread then there is a static method curentThread() of the Thread class, which returns the reference of the thread in which it is called. Its general form is shown here: static Thread current Thread()

Example:

Output

Current thread: Thread [main, 5, main]

New name of main Thread: Thread [Main Thread Demo, 5, main]

1 2 3 4 5 6 7 8 9 10

Output is showing three line statement. In the first statement within square brackets "[]" the first value is the name of thread "main", the second is priority "5" and the last is the name of threadgroup "main", the current (main) thread belongs to. Similarly, in the second printed statement first value is the name of the thread after modification "Main Thread Demo", the second is priority "5" and the last is the thread group name "main". The default created group by Java is "main". In the last it will print 1 to 10 and each digit after 0.5 seconds. In the above example, setName() is used to change the name of the thread. You can get the name of the thread by calling getName(). Both of these methods are of Thread class. Their prototype is given below:

final void setName (String threadName)

final String getName()

To give a pause to the thread sleep() method is used in the given example. The argument passed to the sleep() method is in milliseconds. There are two versions of sleep() methods of Thread class as follows:

static void sleep (long time_in_milliseconds) throws InterruptedException

static void sleep (long time_in_milliseconds, int additional_nanoseconds)

throws InterruptedException

CREATING A THREAD

In Java, a user-defined thread can be created to implement the Runnable interface. The Runnable interface has only one method run() that you must have to implement in your class, that you want to create as a thread. The run() is the method in which that code is written, which is counted as a thread task.

The content of the run() method will be counted as a thread portion, but the method written outside the run() counts as part of the main thread. Both the new thread and the main thread can run concurrently. The thread terminates with the termination of the run method. Control is returned to the caller method of the run method, or can say from where it starts. The run() method should not be called directly, the start() method of the thread class is called, and that is responsible for calling run() for the corresponding thread.

Another way to create a thread is to inherit the Thread class. Thread class has several methods. Few of them are static and few are non-static. As per the need you can override non-static methods. But the run() method must override, because it defines new thread functionality.

EXTENDING THREAD

A new thread is created by creating a new class that extends Thread and then creates an instance of that class. The extending class must override the method run(), which is the entry point of the new thread (user-defined). To begin the execution of the new thread, it must call the start() method. Inside the run() method, you'll write the code, which is considered as the functionality of the thread. The run() method can call other methods of the same class or other class using their objects. It is an entry point of the thread. As the run() method returns (exit or terminate) the thread ends.

Example:


There are two output of the same thread code. Because it is not always sure that a thread executes in the same manner. The output can be same or vary every time. The super() inside the NewThreadDemo constructor invokes the following form of Thread constructor:

public Thread (String thread_Name)

IMPLEMENTING RUNNABLE

By implementing the Runnable interface the thread creation is the easiest way. You can construct a thread on any type of object that implements Runnable. To implement the Runnable interface, a new class needs only to implement the run() method. When you implement the Runnable interface then create an instance of Thread from within that class. The Thread class has several constructors.

public Thread( )

public Thread ( String thread Name)

public Thread (Runnable threadobi)

public Thread (Runnable threadobi, string thread_Name)

public Thread (ThreadGroup groupobj, Runnable threadobj )

public Thread (ThreadGroup groupobj, Runnable threadObj, string name)

After the new thread is created, the start( ) method has to be used to start running, otherwise it will not be started. In essence, start( ) executes a call to run( ).

Example:



MULTITHREADING

Till the last section, we've studied a single thread that is created using the Runnable interface or Thread class. You can spawn as many threads as you want.

Example:


Four chìld threads are created in the program, they run in the order in which those get the CPU time by the run-time system.

About join() and isAlive()

In the immediately above example there is a problem that the main thread is exited before its child thread. Conceptually and logically the parent thread must die or exit after all its child thread exits. Maybe the parent thread works have been completed, but still, it has to wait for its child thread to complete its task and exit. This can be possible, by join() method, All child threads have to join, to prevent the main or parent thread exit before them. Now main or parent thread must wait until all child thread exits.

The isAlive () method is used to determine whether a given thread is finished or running. If isAlive() returns true means the thread is running, else returns false means the thread is finished.

Example:

THREAD PRIORITIES

Priority allows the scheduler to make the decision about when the thread should be allowed to run, and in which order. The higher-priority threads get more CPU time than lower-priority threads. A lower-priority thread can be preempted by higher higher-priority thread. The threads having equal priority get equal CPU time. To set a thread's priority, use the setPriority() method, which belongs to the Thread class. Prototypes are as

final void setPriority (int priorityValue)

Here, priorityValue gives the new priority for the calling thread. The value of priorityValue must be

within the range MIN_PRIORITY and MAX_PRIORITY. The values of these Constants are 1 and 10, respectively. A thread default priority is NORM_PRIORITY, which is currently 5.

These priority constants are defined as final variables within Thread. You can get the current priority value of the thread by calling the getPriority( ) method of Thread, shown here.

final int getPriority( )

Example:

public class Main

{public void setPrioritiesOnThreads ()

{

Thread thread1 = new Thread (new TestThread (1) );

Thread thread2 = new Thread (new TestThread(2 ) );

thread1.start ();

thread2.start () ;

try

{

thread1.join() ;

threađ2. join() ;

}

catch (InterruptedException ex)

{

ex. print StackTrace ();

}

System.out.println ("Done.");

}

public static void main (String [] args )

{

new Main() . setPriorities0nThreads () ;

}

class TestThread implements Runnable

{

int id;

public Test Thread (int id)

{

this. id = id;

}

public void run()

{

for (int i 1; i < 10; i++)

{

System. out. println ( "Thread" +id +":"+ i);

}

}

}

}

Output:

 

The output is without priority assigning to the thread. Both thread are executing not in exact order, but executing concurrently. If we assign the priority to the thread then the code is given below.

Example:

public class Main

{public void setPrioritiesOnThreads ()

{

Thread thread1= new Thread(new TestThread(1) ) ;

Thread thread2 = new Thread (new Test Thread (2) );

thread1 . setPriority (Thread . MAX_ PRIORITY) ;

thread2 . setPriority (Thread . MIN_PRIORTY) ;

threadl . start () ;

thread2 . start () ;

try

{

thread1. join() ;

thread2 . join () ;

}

catch (InterruptedException ex)

{

ex.printStackTrace () ;

}

System. out .println ("Done. ");

}

public static void main (String[ ]args)

{

new Main () . setPrioritiesOnThreads ();

}

class TestThread implements Runnable

{

int iđ;

public TestThread(int id)

{

this.id = id;

}

public void run()

{

for (int i = 1; i <= 10; i++)

{

System. out.print ln("Thread" + id + ":" +i);

}

}

}

}

Now thread1 has the highest priority so it executes first.

SYNCHRONIZATION

When two or more threads want to access a shared resource, then it must ensure that the resource will be used by only one thread at an instant time. The mechanism of this process is called synchronization. Synchronization is the concept of the monitor or semaphore. Monitor works as mutex and is restricted to one thread to own a monitor at a given time. As the thread acquires the lock, all the threads that want to acquire the monitor will be suspended. As the first thread exits from the monitor, one thread will acquire the monitor from the waiting list of threads.

Synchronization of code can be done in two ways, but both use synchronized keywords.

·         Using synchronized method

·         Using synchronized statement

Example:

public class SynchronizedMethodDemo extends object

{

Private static int c=1;

Private static void print (String msg)

{

String threadName= Thread. currentThread () .getName();

System.out.println (threadName + ":"+msg);

}

public static synchronized int getCount ()

{int i=c;

c++;

return i;

}

public static void main (String [] args)

{

try

{

Runnable runnable = new Runnable () { public void run()

{

System.out.println ("count=" + getCount ());

}

};

Thread thread1 = new Thread (runnable, "Thread1") ;

thread1. start ();

Thread. sleep (500) ;

Thread thread2 = new Thread (runnable, "Thread2 ") ;

thread2. start () ;

Thread. sleep (500) ;

Thread thread3=

new Thread (runnable, "Thread3") ;

thread3 . start () ;

Thread.sleep(500);

Thread thread4= new Thread (runnable, "Thread4");

thread4.start();

}

catch (Exception x)

{

}

}

}

 INTER-THREAD COMMUNICATION

If we talk about the mechanism of synchronization, then as one thread exits from the monitor it must inform the waiting threads that it has left the monitor, now suspended thread can proceed to acquire, the lock on the resources or enter in the monitor. If that is not possible then the waiting thread will always be in the waiting list. So, to solve this problem threads must communicate with each other. Java provides a set of methods by which they can communicate with each other. Object class has some final methods for such purposes. All these methods can be called only from the synchronized context.

  •   wait( ) tells the calling thread to leave the monitor and go to sleep until some other thread enters the same monitor and calls notify().
  •  notify( ) give a wake up signal or call to the first thread that is called wait( ) on the same object
  • notify All() gives a wake-up signal or call to all the threads that called wait( ) on the on the same object. The highest priority thread will run first.

These methods are declared within Object, as shown here:

final void wait( ) throws InterruptedException

final void notify ()

final void notifyAll( )

Example:






No comments:

Post a Comment

Machine Learning