Friday, January 2, 2015

Multi Threading Part 2

Executing multiple tasks simultaneously is called "Multitasking".

2 types of multitasking:
1. process-based multitasking
2. thread-based multitasking

Process-based multitasking:

Executing several tasks simultaneously. Each task is a separate independent process. 

Example: While writing a Java program in the editor, you can listen to audio songs by MP3 player in the system. At the same time, you can as well download a file from net. All these tasks are executing simultaneously & independent of each other. These all are different processes, hence process-based multitasking.

Thread-based multitasking:

Executing several tasks simultaneously. Each task is an independent part of the same program and is called thread.

Thread-based multitasking is best suited at programmatic level. 
Process-based multitasking is best suited for OS level.

The main objective of multitasking is to improve performance of the system by reducing response time.

Java provides rich built-in API support for multi-threading.

2 ways to define a thread:
  • Extending Thread class
  • Implementing Runnable interface
Extending Thread class Example:

class Mythread extends Thread {
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("Child Thread");
        }
    }
}
public class ExtendThredClassExample {
    public static void main(String[] args) {
        Mythread t = new Mythread();
        t.start();
        for(int i= 0; i < 10; i++) {
            System.out.println("Main Thread");
        }
    }
}


Thread scheduler:

Whenever multiple threads are waiting to get chance for execution, which thread will get chance first is decided by thread scheduler, whose behavior is JVM and vendor dependent. So, we can't predict exact execution order or exact output.

Thread scheduler is part of JVM.

Difference between t.start() and t.run() ????

In the case of t.start(), a new thread is created and that is responsible for executing run() method.

In the case of t.run(), no new thread will be created. It is equal to calling a class method using an object.

Importance of start() method???

To start a thread, the required mandatory activities like registering the thread with thread scheduler will be automatically done by start() method.

Programmer just have to focus on thread job by overriding run() method. Without start() method, there is no chance of starting a new thread.

What if, we are not overriding run() method????

If you are not overriding run() method, then thread class run() method will be executed which is empty implementation.

It is highly recommended to override run() method of Thread class to provide your thread job.

What if, we are overloading run()???

Overloading run() method is possible, but Thread class start() will only call no argument run() only. The overloaded run() method, can be called just like a normal method.

What if, we are overriding start()???

If we override start() method, then start() will be executed just like a normal method call and no new thread will be created.

Override start method and call super class start() :

class Mythread1 extends Thread {
    public void start() {
        super.start();
        System.out.println("Override start method");
    }
    public void run() {
        System.out.println("run method of thread");
    }
}

public class OverrideStartMethodOfThreadClass {
    public static void main(String[] args) {
        Mythread1 t = new Mythread1();
        t.start();
        System.out.println("Main thread");
    }   
}


This will give o/p as:

Override start method
Main thread
run method of thread


Life cycle of thread:

Mythread t = new Mythread();  --> New or Born state

t.start(); --> Ready/Runnable state

If thread scheduler allocates CPU --> Running state, executing run() method

run() method completes --> Dead state

What if, calling t.start() again???

After starting a starting a thread, we are not allowed to restart the same thread once again, otherwise we will get IllegalThreadStateException.

Mythread t = new Mythread();
t.start(); 
.
.
.
t.start(); // calling again, causes IllegalThreadStateException

Good practice: Never override start() method, but always override run() method.

Defining Thread implementing Runnable interface :

class MyRunnable implements Runnable {
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("Child Thread");
        }
    }
}
public class DefineThreadImplementingRunnable {
    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start(); // this will create new thread

        // t.run(); // just calls the Runnable run() method, no new thread will be created
        for(int i= 0; i < 10; i++) {
            System.out.println("Main Thread");
        }
    }
}


Best method:

Define a thread by implementing Runnable interface.

If you define a thread by extending Thread class, you cannot extend any other class. But using Runnable interface, you can extend some other class, while getting the thread functionality. So, using Runnable interface to define a thread is better approach.

Naming of a thread:

Every thread in java has some name, either given by programmer or default by JVM.

We can get and set name of a thread:
  • public final String getName();
  • public final void setName(String name);
public class GetSetThreadNameExample {
    public static void main(String[] args) {
        System.out.println("Current Thread Name: " + Thread.currentThread().getName());
        Thread.currentThread().setName("raghu");
        System.out.println("New name for thread : " + Thread.currentThread().getName());
    }
}


We can get current executing thread reference, by using the following method:

Thread.currentThread()

Thread pirority :

1 to 10  ( 1 is least and 10 is highest )

Thread.MIN_PRIORITY - 1
Thread.NORM_PRIORITY - 5
Thread.MAX_PRIORITY - 10

Thread scheduler will use these priorities while allocating CPU to the threads.

Default priority: only for the main thread is 5. 
But for all other threads, it will be inheriting from the parent. 
  • public final int getPriority();
  • public final void setPriority(int p);
Mythread t = new Mythread();
t.setPriority(10);
t.start();

The methods to prevent thread Execution????

yield() method :

yield() - pause current thread, give chance to waiting threads of same priority

yield() method causes to pause current executing thread for giving the chance to remaining waiting threads of same priority

If there are no waiting threads, or all waiting threads have low priority then the same thread will continue its execution once again.

The thread which is yielded, when it will get chance again is dependent on thread scheduler.

Join() method : 

If a thread wants to wait until completing some other threads, we should go for join().

Example: Marriage event

Venue fixing ( t1) 
Cards printing (t2) --> this has to call t1.join() because unless venue fixing is completed, we can not execute cards printing.
Cards distributing (t3) --> t2.join(). You cannot distribute until you complete cards printing.

join() method is overloaded to specify timeout.

Every join() method throws InterruptedException. Hence, whenever we are using join() method, we should handle InterruptedException either by try-catch or by throws.

class Mythread extends Thread {
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("Child Thread");
        }
    }
}
public class ExtendThredClassExample {
    public static void main(String[] args) throws InterruptedException {
        Mythread t = new Mythread();
        t.start(); // this will create new thread       
        t.join(); // main thread will not execute until child thread completes

        Thread.sleep(5000);  // this is in milli seconds
        for(int i= 0; i < 10; i++) {
            System.out.println("Main Thread");
        }
    }
}


Sleep() method : 

If a thread don't want to perform any operation for a particular period of time(just pausing), then we should go for sleep.

Whenever we are using sleep() method, we should compulsorily handle InterruptedException other wise we will get compile error.

Thread.sleep(5000); // the time out is in millis

Interruption of a thread:

A thread can interrupt another sleeping or waiting thread. For this, you can use interrupt() method.

You need to catch InterruptException to handle this.

Whenever we are calling interrupt() method, if the target thread is not in sleeping or waiting state, then there is no impact immediately. Interrupt call will wait until target thread entered into sleeping or waiting state. Once target thread entered into sleeping or waiting state, the interrupt call will impact the target thread.



Synchronized modifier : 

synchronized modifier is applicable only for methods and blocks. You cannot use this for classes and variables.

this modifier makes a method or block to allow only one thread at a time to execute.

We can resolve data-inconsistency problem, but it increases waiting time of the threads and effects performance of the system.

Every object in Java has a unique lock associated with it. 'synchronized' modifier is internally implemented using this lock. Using synchronized keyword on a method or block uses this lock.

lock concept is based on object. So, if a thread is executing a synchronized method on a given object, the remaining threads are not allowed to execute any synchronized method on the same object simultaneously. Because, lock is associated with given object.

Whenever multiple threads are operating on same object then only synchronization play the role. If multiple threads are operating on multiple objects, there is no impact of synchronization.

Every class in Java has unique class level lock. 
If a thread wants to execute a static synchronized method, it requires class level lock.

While a thread is executing a static synchronized method, then remaining threads are not allowed to execute any other static synchronized methods of that class simultaneously. 

There is no link between class level lock and object level lock. Both are different.

Synchronized block : 

If very few lines of code requires synchronization, then it is never recommended to declare entire method as synchronized.

main advantage is, it reduces waiting time of the threads and improve performance.

1. We can declare synchronized block to get current object lock as:
            synchronized(this) {  .... }  --> current object level lock

2. To get lock of particular object, say b, declare as :

            synchronized(b) { ...... }  --> get lock of object b

3. To get class level lock, declare as: 
            synchronized(classname.class) { ....... } --> class level lock

A thread can acquire more than one lock at a time.


Inter-thread communication???

Two threads will communicate with each other by using waith(), notify(), notifyAll() methods.

The thread which requires updation will call wait().
The thread which is responsible to update the other thread has to call notify().

If a thread wants to call, waith(), notify(), notifyAll() compulsory the thread should be the owner of that object. That means, the thread has to get lock of that object. This indicates, thread should be in the synchronized area.

Tip: We should call wait(), notify(), notifyAll() methods only from synchronized area otherwise we will get runtime exception saying IllegalMonitorStateException.

If a thread calls, wait() method it releases the lock immediately. It releases the lock only current object, not all locks.

wait() method throws InterruptedException.






 












No comments:

Post a Comment