Monday, 29 January 2018

7.0 MultiThreading in Java

Multitasking - executing multiple tasks simultaneously.

  • process based multitasking
Executing multiple task simultaneously where each task is separate process
  • Thread based multitasking
Executing multiple task simultaneously where each task is independent but part of same program.

main advantage is to increase performance and to reduce response time of system.


MULTITHREADING - Multiple threads executing simultaneously

Thread : A light weight process.
A separate flow of execution.

Defining Thread -  
  1. By extending Thread class
  2. By implementing Runnable interface



class ThreadDemo
{
public static void main(String args[])
{
MyThread obj=new MyThread();
obj.start();
for(int i=1;i<=10;i++)
{
System.out.println("Main Thread : " +i);
}
}
}
class MyThread extends Thread
{
public void run()
{
for(int i=1;i<=10;i++)
{
System.out.println("Child Thread : " +i);
}
}
}


C:\Users\Aarchi\Desktop\Java Development>java ThreadDemo
Main Thread : 1
Main Thread : 2
Main Thread : 3
Child Thread : 1
Main Thread : 4
Main Thread : 5
Child Thread : 2
Main Thread : 6
Child Thread : 3
Main Thread : 7
Child Thread : 4
Main Thread : 8
Child Thread : 5
Main Thread : 9
Child Thread : 6
Main Thread : 10
Child Thread : 7
Child Thread : 8
Child Thread : 9
Child Thread : 10




Thread scheduler - It is a part of JVM and responsible for threads execution.
In which order threads will execute is decide by thread scheduler. 

Overloading of run() - is always possible but thread class start() can invoke no-arg run ().
The other overloaded method can be invoked explicitly like a normal method call.

If you'll not override run() in you class then Thread class run() will get executed which has empty implementation hence we'll not get any output.

If we override start() then our start() will be executed just like a normal method call and new thread won't be created.

THREAD LIFE CYCLE









After starting a thread if we are trying to restart the same thread then we'll get run time exception.
saying IllegalThreadStateException.

Thread t=new Thread();
t.start();
t.start();


Defining a thread by implementing Runnable Interface.

Runnable interface present in java.lang package and only contain one method run().



import java.lang.*;
class ThreadDemo1
{
public static void main(String args[])
{
MyRunnable obj=new MyRunnable();
Thread t=new Thread(obj);
t.start();
for(int i=1;i<=10;i++)
{
System.out.println("Main Thread : "+i);
}
}
}

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

C:\Users\Aarchi\Desktop\Java Development>java ThreadDemo1
Main Thread : 1
Main Thread : 2
Main Thread : 3
Main Thread : 4
Main Thread : 5
Child Thread : 1
Child Thread : 2
Main Thread : 6
Child Thread : 3
Main Thread : 7
Child Thread : 4
Main Thread : 8
Child Thread : 5
Main Thread : 9
Main Thread : 10
Child Thread : 6
Child Thread : 7
Child Thread : 8
Child Thread : 9
Child Thread : 10


CASE STUDY

NOTE : implementation of Runnable interface is recommended
If we'll extend Thread class then we can't extend any other class.


THREAD CLASS CONSTRUCTORS
  1. Thread t = new Thread();
  2. Thread t = new Thread(Runnable r);
  3. Thread t = new Thread(String name);
  4. Thread t = new Thread(Runnable r, String name);
  5. Thread t = new Thread(ThreadGroup g, String name);
  6. Thread t = new Thread(ThreadGroup g, Runnable r);
  7. Thread t = new Thread(ThreadGroup g, Runnable r, String name);
  8. Thread t = new Thread(ThreadGroup g, Runnable r, String name, Long stacksize);
class MyThread extends Thread

MyThread obj=newMyThread();
Thread t=new Thread(obj);
t.start();

Thi is valid
MyThread ------>Thread------->Runnable

  1. Thread t = new Thread(Runnable r);
we can pass MyThread object here.



To get and set Thread names

class ThreadDemo2
{
public static void main(String args[])
{
System.out.println(Thread.currentThread().getName());
MyThreada obj=new MyThreada();
System.out.println(obj.getName());
Thread.currentThread().setName("Bikesh");
System.out.println(Thread.currentThread().getName());
System.out.println(10/0);
}
}
class MyThreada extends Thread
{
}


C:\Users\Aarchi\Desktop\Java Development>java ThreadDemo2
main
Thread-0
Bikesh
Exception in thread "Bikesh" java.lang.ArithmeticException: / by zero
        at ThreadDemo2.main(ThreadDemo2.java:10)


Thread Priorities
Every thread in java have some priority.
Range of Priorities - 1(min) to 10(max)
Thread.MIN_PRIORITY=1
Thread.NORM_PRIORITY=5
Thread.MAX_PRIORITY=10
Thread.LOW_PRIORITY - Invalid

Thread scheduler will set the priority while allocating processor.
Thread which is having highest priority will get the chance first.
If two threads have same priority then we can't expect exact execution order. It depends on Thread scheduler.

Thread class defines the following methods to get and set priority of the thread.
public final int getPriority();
public final void setPriority(int p);
where p ranges from 1 to 10.
if it is not then we'll get IllegalArgumentException.


default priority for every thread is 5. (InCorrect) default priority of all thread inherited from parent to child. i.e whatever priority parent thread has , the same priority will be there for the child thread.
OR
default priority for only main thread is 5. (Correct)


class ThreadDemo2
{
public static void main(String args[])
{
System.out.println("Priority of "+Thread.currentThread().getName() +" = "+Thread.currentThread().getPriority());
Thread.currentThread().setPriority(8);
MyThreada obj=new MyThreada();
System.out.println("Priority of "+Thread.currentThread().getName() +" = "+Thread.currentThread().getPriority());
System.out.println("Priority of "+obj.getName() +" = "+obj.getPriority());
}
}
class MyThreada extends Thread
{
public void run(){}
}


C:\Users\Aarchi\Desktop\Java Development>javac ThreadDemo2.java

C:\Users\Aarchi\Desktop\Java Development>java ThreadDemo2
Priority of main = 5
Priority of main = 8
Priority of Thread-0 = 8

Thread.currentThread().setPriority(18);

Exception in thread "main" java.lang.IllegalArgumentException
        at java.lang.Thread.setPriority(Thread.java:1089)
        at ThreadDemo2.main(ThreadDemo2.java:10)


Some platforms won't provide support for thread priorities.
In Spite of setting highest priority to a thread , you may not get the desired output.


PREVENT THREAD EXECUTION
  1. yield()
  2. join()
  3. sleep()
yield() - causes to pass current executing thread to give the chance for waiting thread of same priority.
if there is no waiting thread or all waiting threads have low priority then same thread can continue its execution.
The thread which is yielded, when it will get the chance once again depends upon thread scheduler.
We can't expect exact immediate regaining the execution engine.

join() -
If a thread wants to wait until completing some other thread then we should go for join().
for ex. if a thread t1 wants to wait until completing t2 then t1 has to call t2.join()
If t1 executes t2.join() then  immediately t1 will be entered into waiting state until t2 completes.
Once t2 completes then t1 can continue its execution.

public void final join() throws InterruptedException;
public void final join(long ms) throws InterruptedException;

public void final join(long ms , in ns) throws InterruptedException;

while a thread waiting until completing some other thread then there may be a chance interrupted exception. Therefore every join method throws InterruptedException which is checked exception exception compulsory we should handle this exception either by using try catch or by throws keyword.

Example to get main thread to apply join

class MyThreadDemo extends Thread
{
static Thread mt;
public void run()
{
try{
mt.join();
}
catch(InterruptedException ie)
{
System.out.println(ie.getMessage());
}
for(int i=1;i<=10;i++)
{
System.out.println("Child Thread : "+i);
}
}
}

class ThreadDemo3
{
public static void main(String args[])
{
MyThreadDemo.mt=Thread.currentThread();
MyThreadDemo obj=new MyThreadDemo();
obj.start();
for(int i=1;i<=10;i++)
{
System.out.println("Main Thread : "+i);
}
}
}

If main thread also put obj.join() then a deadlock situation will arise.
If main thread put join on main thread itself then also deadlock situation will arise.
Thread.currentThread().join();

sleep() - If a thread don't want to perform any operation for a particular amount of time then we should go for sleep().
public static native void sleep(long ms) throws InterruptedException;
public static void sleep(long ms, int ns) throws InterruptedException;
Every sleep method throws IE Exception, which is checked exception hence whenever we are using sleep method , compulsory we should handle InterruptedException either by try catch or by throws keyword.

class ThreadDemo4
{
public static void main(String args[]) throws InterruptedException
{
for(int i=1;i<=10;i++)
{
System.out.println("Slider = "+i);
Thread.sleep(5000);
}
}
}


interrupt() - example of how a thread can interrupt another thread

class ThreadDemoInterrupt extends Thread
{
public void run()
{
try{
for(int i=1;i<=10;i++)
{
System.out.println("Lazy Thread = "+i);
Thread.sleep(2000);
}
}
catch(InterruptedException ie)
{
System.out.println("I got interrupted");
}
}
}

class ThreadDemo4
{
public static void main(String args[])
{
ThreadDemoInterrupt obj=new ThreadDemoInterrupt();
obj.start();
obj.interrupt();
System.out.println("End of main THread");
}
}

Note : Whenever we are calling interrupt() if the target thread not in sleeping state or waiting state then there is no impact of interrupt call immediately. Interrupt call will wait until target thread enter into sleeping or waiting state. If the target thread enter into sleeping or waiting state , immediately interrupt call will interrupt the target thread. 
If the target thread never entered into sleep or waiting state in its lifetime then there is no impact of interrupt call. This is the only case where interrupt call will be wasted.
























Synchronization - It is a modifier .It is only applicable for methods and block but not for classes and variables

If multiple threads try to operate simultaneously on a single java object then this type of problem is called object inconsistency problem.

To resolve data inconsistency we can use synchronized keyword.

if a method or block declared as synchronized then at a time only one thread is allowed to execute that method or block on the given object.

The main advantage of synchronized keyword is , we can resolve data inconsistency problems but the main disadvantage is , it increases waiting time of threads and creates performance problems. Hence if there is no specific requirement then it is not recommended to use synchronized keyword.

Internally synchronization concept is implemented by using lock.
Every object in java has a unique lock. Whenever we are using synchronized keyword then only lock concept will come into picture.
If a thread wants to execute a synchronized method on the given object , first it has to get lock of that object.
Once thread got the lock then it is allowed to execute any synchronized method on that object. 
Once method execution completes , automatically thread releases lock. So acquiring and releasing lock internally takes care by JVM.



















t1 got the lock and start execution of m1()
while t1 was executing t2 came but it will go into waiting state.
t3 came to access m2 method. But to execute m2 object lock is required which is currently with t1 therefore t3 will also go into waiting state.
t4 came to access m3, it will directly able to execute because no lock is required to execute a non-synchronized method.


NON-SYNCHRONIZED EXAMPLE

class NonSync
{
public static void main(String args[])
{
Display obj=new Display();
MyThr t1=new MyThr(obj,"Bikesh");
MyThr t2=new MyThr(obj,"Amitesh");
MyThr t3=new MyThr(obj,"Maurya");
t1.start();
t2.start();
t3.start();
}
}

class Display
{
public void wish(String name)
{
for(int i=1;i<=10;i++)
{
System.out.println("Good Morning : " + name);
try{
Thread.sleep(1000);
}
catch(InterruptedException ie)
{}
}
}
}

class MyThr extends Thread
{
Display d;
String name;
public MyThr(Display d,String name)
{
this.d=d;
this.name=name;
}
public void run()
{
d.wish(name);
}
}

C:\Users\Aarchi\Desktop\Java Development>javac NonSync.java

C:\Users\Aarchi\Desktop\Java Development>java NonSync
Good Morning : Amitesh
Good Morning : Maurya
Good Morning : Bikesh
Good Morning : Amitesh
Good Morning : Bikesh
Good Morning : Maurya
Good Morning : Amitesh
Good Morning : Maurya
Good Morning : Bikesh
Good Morning : Amitesh
Good Morning : Maurya
Good Morning : Bikesh
Good Morning : Maurya
Good Morning : Bikesh
Good Morning : Amitesh
Good Morning : Maurya
Good Morning : Bikesh
Good Morning : Amitesh
Good Morning : Maurya
Good Morning : Amitesh
Good Morning : Bikesh
Good Morning : Maurya
Good Morning : Amitesh
Good Morning : Bikesh
Good Morning : Maurya
Good Morning : Amitesh
Good Morning : Bikesh
Good Morning : Maurya
Good Morning : Amitesh
Good Morning : Bikesh


SYNCHRONIZED EXAMPLE

just add the synchronized modifier in the method
public synchronized void wish(String name)

C:\Users\Aarchi\Desktop\Java Development>javac NonSync.java

C:\Users\Aarchi\Desktop\Java Development>java NonSync
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Maurya
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Bikesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh
Good Morning : Amitesh

If multiple thread operating on same object then only synchronization required.
In below case we have created two different object and t1 and t2 will lock obj1 and obj2, therefore synchronization is not required here. In spite of having synchronize method we'll get irregular output.

class NonSync
{
public static void main(String args[])
{
Display obj1=new Display();
                Display obj2=new Display();
MyThr t1=new MyThr(obj1,"Bikesh");
MyThr t2=new MyThr(obj2,"Amitesh");
t1.start();
t2.start();
}
}

static synchronized method will resolve this issue
public static synchronized void wish(String name)

Class level lock - every class in java has a unique lock which is also known as class level lock. 

If a thread wants to execute any static synchronized method then it require class level lock is required instead of object lock required.
























class bank
{
public static void main(String args[])
{
locker l1=new locker();
locker l2=new locker();
MyThreadDemoBank1 t1=new MyThreadDemoBank1(l1,"Bikesh");
MyThreadDemoBank2 t2=new MyThreadDemoBank2(l2,"Amitesh");
t1.start();
t2.start();
}
}

class locker
{
static int amount=1000;
static int translimit=0;
public static synchronized void account(String name)
{
while (amount > 0)
{
translimit++;
int total=amount;
amount=amount-100;
try
{
Thread.sleep(1000);
}
catch (InterruptedException ie)
{
}
System.out.println("Transaction : "+total+" - 100 ( "+name+" withdraw) ="+amount);
if(translimit==5)
{translimit=0;break; }
}
}
}

class MyThreadDemoBank1 extends Thread
{
String name;
locker l;
public MyThreadDemoBank1(locker l,String name)
{
this.l=l;
this.name=name;
}
public void run()
{
l.account(name);
}
}

class MyThreadDemoBank2 extends Thread
{
String name;
locker l;
public MyThreadDemoBank2(locker l,String name)
{
this.l=l;
this.name=name;
}
public void run()
{
l.account(name);
}
}

C:\Users\Aarchi\Desktop\Java Development>javac bank.java

C:\Users\Aarchi\Desktop\Java Development>java bank
Transaction : 1000 - 100 ( Amitesh withdraw) =900
Transaction : 900 - 100 ( Amitesh withdraw) =800
Transaction : 800 - 100 ( Amitesh withdraw) =700
Transaction : 700 - 100 ( Amitesh withdraw) =600
Transaction : 600 - 100 ( Amitesh withdraw) =500
Transaction : 500 - 100 ( Bikesh withdraw) =400
Transaction : 400 - 100 ( Bikesh withdraw) =300
Transaction : 300 - 100 ( Bikesh withdraw) =200
Transaction : 200 - 100 ( Bikesh withdraw) =100
Transaction : 100 - 100 ( Bikesh withdraw) =0


SYNCHRONIZED BLOCK
If very few lines of the code required synchronization then it is not recommended to declare entire method as synchronized. We have to enclose those few lines of the code by using synchronized block. 

The main advantage of synchronized block over synchronized method is it reduces waiting time of threads and improves performance of the system/application.

synchronized(this)
{
..............
...............
..............
}
this is use to get the lock of current object

to get lock of particular project
synchronized(b)
{
.............
.............
.............
}
if a thread got the lock of object b then only it is allowed to execute this block.

to get class level lock
synchronized(Display.class)
{
...............
..............
..............
}
if a thread want to get the class level lock of class Display.

Lock concept only applicable for object types and class types but not for primitives.Hence we can't pass primitive type as argument to synchronized block otherwise we'll get compile time error saying "unexpected type fount int required ref."

int x=1;
ex: 
synchronize(x)
{
..............
...........
...............
}


QUICK REVISION:
  1. What is synchronized keyword and where we can apply ?
  2. Explain advantage of synchronized keyword.
  3. Explain disadvantage of synchronized keyword.
  4. What is race condition ? (data inconsistency problem)
  5. What is object lock and when it is required ?
  6. What is class level lock and when it is required ?
  7. Difference between class level and object level lock ?
  8. What is synchronized block ?
  9. How to declare synchronized block to get lock of current object, class level lock ?
  10. what is the advantage of synchronize block over synchronized method.
  11. is a thread can acquire multiple locks simultaneously ? (Yes)  



No comments:

Post a Comment