Introduction to concurrency and threading in Java

0
93

Concurrency

Concurrency is the ability to run several programs or several parts of a program in parallel. If a time consuming task can be performed asynchronously or in parallel, this improves the throughput and the interactivity of the program. In java concurrency is also referred to as multithreading.

Multithreading

Multithreading in Java is a process of executing multiple threads simultaneously.Unlike most other computer languages, 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.

A thread is a lightweight sub-process, the smallest unit of processing. Multiprocessing and multithreading, both are used to achieve multitasking.

However, we use multithreading rather than multiprocessing because threads use a shared memory area. They don’t allocate a separate memory area so saves memory, and context-switching between the threads takes less time than process.

Java Multithreading is mostly used in games, animation, etc.

Process vs Threads.

A process runs independently and isolated of other processes. It cannot directly access shared data in other processes. The resources of the process, e.g. memory and CPU time, are allocated to it via the operating system.

A thread is a so called lightweight process. It has its own call stack, but can access shared data of other threads in the same process. Every thread has its own memory cache. If a thread reads shared data, it stores this data in its own memory cache.

A thread can re-read the shared data.

A Java application runs by default in one process. Within a Java application you work with several threads to achieve parallel processing or asynchronous behavior.

Thread States

In Java, a thread always exists in any one of the following states. These states are:

  1. New
  2. Active
  3. Blocked / Waiting
  4. Timed Waiting
  5. Terminated

New: Whenever a new thread is created, it is always in the new state. For a thread in the new state, the code has not been run yet and thus has not begun its execution.

Active: When a thread invokes the start() method, it moves from the new state to the active state. The active state contains two states within it: one is runnable, and the other is running.

  • Runnable: A thread, that is ready to run is then moved to the runnable state. In the runnable state, the thread may be running or may be ready to run at any given instant of time. It is the duty of the thread scheduler to provide the thread time to run, i.e., moving the thread the running state.
  • Running: When the thread gets the CPU, it moves from the runnable to the running state. Generally, the most common change in the state of a thread is from runnable to running and again back to runnable

Blocked or Waiting: Whenever a thread is inactive for a span of time (not permanently) then, either the thread is in the blocked state or is in the waiting state.

For example, when a thread is waiting for I/O to complete, it lies in the blocked state. It’s the responsibility of the thread scheduler to reactivate and schedule a blocked/waiting thread. A thread in this state cannot continue its execution any further until it is moved to runnable state. Any thread in these states does not consume any CPU cycle.

A thread is in the blocked state when it tries to access a protected section of code that is currently locked by some other thread. When the protected section is unlocked, the schedule picks one of the thread which is blocked for that section and moves it to the runnable state. 

Whereas, a thread is in the waiting state when it waits for another thread on a condition. When this condition is fulfilled, the scheduler is notified and the waiting thread is moved to runnable state.

Terminated: A thread reaches the termination state because of the following reasons:

  • When a thread has finished its job, then it exists or terminates normally.
  • Abnormal termination: It occurs when some unusual events such as an unhandled exception or segmentation fault.

A terminated thread means the thread is no more in the system. In other words, the thread is dead, and there is no way one can respawn (active after kill) the dead thread.

Writing Multithreaded programs

Source code:

class MultithreadingDemo extends Thread {

    public void run()

    {

        try {

            // Displaying the thread that is running

            System.out.println(

                “Thread ” + Thread.currentThread().getId()

                + ” is running”);

        }

        catch (Exception e) {

            // Throwing an exception

            System.out.println(“Exception is caught”);

        }

    }

}

public class sample {

public static void main(String[] args)

    {

        int n = 8; // Number of threads

        for (int i = 0; i < n; i++) {

            MultithreadingDemo object

                = new MultithreadingDemo();

            object.start();

        }

    }

}

Output

Threading in Java

Thread Properties

In the following section we will discuss some miscellaneous properties of threads. Thread properties includes: thread priorities, daemon threads, thread groups and handler for uncaught exceptions.

Thread Priorities

In Java programming, every thread has priority. By default, thread inherits the priority of the thread that constructed  it. 

Thread priority of any thread can be increased or decreased using setPriority method. The value of priority can be set to any value between min_Priority(defined as 1 in Thread class) and max_Priority(defined as 10). Norm_Priority is defined as 5.

Whenever a thread scheduler has a chance to pick priority it prefers thread with higher priority.

Thread priorities are highly system dependent. 

Daemon Threads

A thread can be turn to Daemon thread by calling:

t.setDaemon(true);

A daemon is simply a thread that has no other role than to serve others. Examples are timer threads that send regular “timer tricks” to other threads or threads that clean up stale cache entries.

When only daemon threads remain, the virtual machine exits. There is no point in keeping the program running if all remaining threads are daemons. 

A daemon thread should never access a persistent resource such as file or database since it can terminate at any time.

Handlers for uncaught exceptions

The run method of thread cannot throw any checked exceptions, but it can be terminated by an unchecked exception. In that case, the thread dies.

However, there is no catch clause to which exceptions can be propagated. Instead, just before thread dies, the exception is passed to a  handler for uncaught exceptions.

The handler must belong to class that implements the Thread.UncaughtExceptionHandler interface. That interface has single method,

Void uncaughtExceptionHandler(Thread t, Throwable e)

Thread Synchronization

Synchronization in Java is the capability to control the access of multiple threads to any shared resource.

Java Synchronization is better option where we want to allow only one thread to access the shared resource.

The synchronization is mainly used to

  1. To prevent thread interference.
  2. To prevent consistency problems.

There are two types of synchronization.

  1. Thread synchronization
  2. Process synchronization

Thread synchronization are of two types, mutual exclusive and inter-thread communication.

  1. Mutual Exclusive
    1. Synchronized method.
    2. Synchronized block.
    3. Static synchronization.
  2. Cooperation (Inter-thread communication in java)

Mutual Exclusive

Mutual Exclusive helps keep threads from interfering with one another while sharing data. It can be achieved by using the following three ways:

  1. By Using Synchronized Method
  2. By Using Synchronized Block
  3. By Using Static Synchronization

Example of problem without synchronization

Source code:

class Table{  

void printTable(int n){//method not synchronized  

   for(int i=1;i<=5;i++){  

     System.out.println(n*i);  

     try{  

      Thread.sleep(400);  

     }catch(Exception e){System.out.println(e);}  

   }  

 }  

}  

class MyThread1 extends Thread{  

Table t;  

MyThread1(Table t){  

this.t=t;  

}  

public void run(){  

t.printTable(5);  

}  

}  

class MyThread2 extends Thread{  

Table t;  

MyThread2(Table t){  

this.t=t;  

}  

public void run(){  

t.printTable(100);  

}  

}  

public class sample {

public static void main(String[] args) {

Table obj = new Table();//only one object  

MyThread1 t1=new MyThread1(obj);  

MyThread2 t2=new MyThread2(obj);  

t1.start();  

t2.start();  

}

}

Output:

Threading in Java

Example of Java synchronized method

Source code:

class Table{  

synchronized void printTable(int n){//synchronized method  

  for(int i=1;i<=5;i++){  

    System.out.println(n*i);  

    try{  

      Thread.sleep(400);  

    }catch(Exception e){System.out.println(e);}  

  }  

}  

}  

class MyThread1 extends Thread{  

Table t;  

MyThread1(Table t){  

this.t=t;  

}  

public void run(){  

t.printTable(5);  

}  

}  

class MyThread2 extends Thread{  

Table t;  

MyThread2(Table t){  

this.t=t;  

}  

public void run(){  

t.printTable(100);  

}  

}

public class sample {

public static void main(String[] args) {

Table obj = new Table();//only one object  

MyThread1 t1=new MyThread1(obj);  

MyThread2 t2=new MyThread2(obj);  

t1.start();  

t2.start();  

}

}

Output:

Threading in Java

Learn more about file handling in Java.

Facebook Comments