2017  Kodetalk | Feedback | Privacy Policy | Terms | About
userimage

understanding multithreading in java with example for dummies

Multitasking: Executing several tasks simultaneously is the concept of multitasking. There are two types of multitasking’s.
1)    Process based multitasking.
2)    Thread based multitasking.
Diagram:



Process based multitasking: Executing several tasks simultaneously where each task is a separate independent process such type of multitasking is called process based multitasking.
Example:
1)     While typing a java program in the editor we can able to listen mp3 audio songs at the same time we can download a file from the net all these tasks are independent of each other and executing simultaneously and hence it is Process based multitasking.
2)     This type of multitasking is best suitable at "os level".


Thread based multitasking: Executing several tasks simultaneously where each task is a separate independent part of the same program, is called Thread based multitasking. And each independent part is called a "Thread".
1)    This type of multitasking is best suitable for “programatic level”.
2)    When compared with “C++”, developing multithreading examples is very easy in java because java provides in built support for multithreading through a rich API (Thread, Runnable, ThreadGroup, ThreadLocal….etc).
3)   In multithreading on 10% of the work the programmer is required to do and 90% of the work will be down by java API.


      The main important application areas of multithreading are:
               1)    To implement multimedia graphics.
               2)    To develop animations.
               3)    To develop video games etc.
4)   Whether it is process based or Thread based the main objective of multitasking is to improve performance of the system by reducing response time.


The ways to define instantiate and start a new Thread:

What is singleton? Give example?
We can define a Thread in the following 2 ways.
1.    By extending Thread class.
2.    By implementing Runnable interface.
Defining a Thread by extending “Thread class”:
Example:



public class ThreadDemo
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();//Instantiation of a Thread
		t.start();//starting of a Thread
		
		for(int i=0;i<5;i++)
		{
			System.out.println("main thread");
		}
	}
}

Case 1: Thread Scheduler:
1)    If multiple Threads are waiting to execute then which Thread will execute 1st is decided by "Thread Scheduler" which is part of JVM.
2)    Which algorithm or behavior followed by Thread Scheduler we can’t expect exactly it is the JVM vendor dependent hence in multithreading examples we can’t expect exact execution order and exact output.
The following are various possible outputs for the above program.


Case 2: Difference between t.start() and t.run() methods.


In the case of t.start() a new Thread will be created which is responsible for the execution of run() method. But in the case of t.run() no new Thread will be created and run() method will be executed just like a normal method by the main Thread. In the above program if we are replacing t.start() with t.run() the following is the output.


Output:
child thread
child thread
child thread
child thread
child thread
child thread
child thread
child thread
child thread
child thread
main thread
main thread
main thread
main thread
main thread


Entire output produced by only main Thread.


Case 3:  importance of Thread class start() method.


For every Thread the required mandatory activities like registering the Thread with Thread Scheduler will takes care by Thread class start() method and programmer is responsible just to define the job of the Thread inside run() method. That is start() method acts as best assistant to the programmer.


Example:

start()
{
	// Register Thread with Thread Scheduler
	// All other mandatory low level activities.
	// Invoke or calling run() method.
}


We can conclude that without executing Thread class start() method there is no chance of starting a new Thread in java.


Case 4:  If we are not overriding run() method:
If we are not overriding run() method then Thread class run() method will be executed which has empty implementation and hence we won’t get any output.
Example:

piblic class MyThread extends Thread 
{

}
public class ThreadDemo
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();
		t.start();
	}
}

It is highly recommended to override run() method. Otherwise don’t go for multithreading concept.


Case 5:Overriding of run() method.
We can overload run() method but Thread class start() method always  invokes no argument run() method the other overload run() methods we have to call explicitly then only it will be executed just like normal method.
Example:

public class MyThread extends Thread
{
	public void run()
	{
		System.out.println("no arg method");
	}
	public void run(int i)
	{
		System.out.println("int arg method");
	}
}
public class ThreadDemo
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();
		t.start();
	}
}

Output:
No arg method


Case 6:  overriding of start() method:
If we override start() method then our start() method will be executed just like a normal method call and no new Thread will be started.
Example:

public class MyThread extends Thread
{
	public void start()
	{
		System.out.println("start method");
	}
	public void run()
	{
		System.out.println("run method");
	}
}
public class ThreadDemo
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();
		t.start();
		System.out.println("main method");
	}
}

Output:
start method
main method
Entire output produced by only main Thread.


Case 7:
Example 1:

Example 2:



Output:


Case 8:
life cycle of the Thread:


Diagram:



Once we created a Thread object then the Thread is said to be in new state or born state.
Once we call start() method then the Thread will be entered into Ready or Runnable state.
If Thread Scheduler allocates CPU then the Thread will be entered into running state.
Once run() method completes then  the Thread will entered into dead state.


Case 9:


After starting a Thread we are not allowed to restart the same Thread once again otherwise we will get runtime exception saying “IllegalThreadStateException”.
Example:
MyThread t=new MyThread();
t.start();  //valid
;;;;;;;;
t.start();//we will get R.E saying: IllegalThreadStateException

Defining a Thread by implementing Runnable interface:



We can define a Thread even by implementing Runnable interface also. Runnable interface present in java.lang.pkg and contains only one method run().
Diagram:


Example:




public class ThreadDemo
{
	public static void main(String[] args)
	{
		MyRunnable r=new MyRunnable();
		Thread t=new Thread(r);//here r is a Target Runnable
		t.start();
	
		for(int i=0;i<10;i++)
		{
			System.out.println("main thread");
		}
	}
}

Output:
main thread
main thread
main thread
main thread
main thread
main thread
main thread
main thread
main thread
main thread
child Thread
child Thread
child Thread
child Thread
child Thread
child Thread
child Thread
child Thread
child Thread
child Thread


We can’t expect exact output but there are several possible outputs.


Case study:

MyRunnable r=new MyRunnable();
Thread t1=new Thread();
Thread t2=new Thread(r);


Case 1:     t1.start():
A new Thread will be created which is responsible for the execution of Thread class run()method.


Output:


main thread
main thread
main thread
main thread
main thread


Case 2:  t1.run():

No new Thread will be created but Thread class run()  method will be executed just like a normal method call.


Output:


main thread
main thread
main thread
main thread
main thread


Case 3:  t2.start():


New Thread will be created which is responsible for the execution of MyRunnable run() method.


Output:


main thread
main thread
main thread
main thread
main thread
child Thread
child Thread
child Thread
child Thread
child Thread


Case 4:    t2.run():

No new Thread will be created and MyRunnable run()  method will be executed just like a normal method call.


Output:

child Thread
child Thread
child Thread
child Thread
child Thread
main thread
main thread
main thread
main thread
main thread


Case 5:     r.start():

We will get compile time error saying start()method is not available in MyRunnable class.


Output:



Compile time error
E:\SCJP>javac ThreadDemo.java
ThreadDemo.java:18: cannot find symbol
Symbol: method start()
Location: class MyRunnable


Case 6:    r.run():


No new Thread will be created and MyRunnable class run() method will be executed just like a normal method call.


Output:



child Thread
child Thread
child Thread
child Thread
child Thread
main thread
main thread
main thread
main thread
main thread


Best approach to define a Thread:

Among the 2 ways of defining a Thread, implements Runnable approach is always recommended.
In the 1st approach our class should always extends Thread class there is no chance of extending any other class hence we are missing the benefits of inheritance.
 But in the 2nd approach while implementing Runnable interface we can extend some other class also. Hence implements Runnable mechanism is recommended to define a Thread.
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);


Another approach to define a Thread(not recommended to use):



Output:

main method
run method


Getting and setting name of a Thread:
Every Thread in java has some name it may be provided explicitly by the programmer or automatically generated by JVM.
 Thread class defines the following methods to get and set name of a Thread.
Methods:
1)    public final String getName()
2)    public final void setName(String name)


Example:


public class MyThread extends Thread
{
}
public class ThreadDemo
{
	public static void main(String[] args)
	{
		System.out.println(Thread.currentThread().getName());//main
		MyThread t=new MyThread();
		System.out.println(t.getName());//Thread-0
		Thread.currentThread().setName("Bhaskar Thread");
		System.out.println(Thread.currentThread().getName());//Bhaskar Thread
	}
}

Note: We can get current executing Thread object reference by using Thread.currentThread() method.
Thread Priorities


Thread Priorities:


Every Thread in java has some priority it may be default priority generated by JVM (or) explicitly provided by the programmer.
The valid range of Thread priorities is 1 to 10[but not 0 to 10] where 1 is the least priority and 10 is highest priority.


Thread class defines the following constants to represent some standard priorities.


1)    Thread. MIN_PRIORITY----------1
2)    Thread. MAX_PRIORITY----------10
3)    Thread. NORM_PRIORITY--------5


There are no constants like Thread.LOW_PRIORITY, Thread.HIGH_PRIORITY
Thread scheduler uses these priorities while allocating CPU.
The Thread which is having highest priority will get chance for 1st execution.
If 2 Threads having the same priority then we can’t expect exact execution order it depends on Thread scheduler whose behavior is vendor dependent.
We can get and set the priority of a Thread by using the following methods.
1)    public final int getPriority()
2)    public final void setPriority(int newPriority);//the allowed values are 1 to 10
The allowed values are 1 to 10 otherwise we will get runtime exception saying “IllegalArgumentException”.


Default priority:

The default priority only for the main Thread is 5. But for all the remaining Threads the default priority will be inheriting from parent to child. That is whatever the priority parent has by default the same priority will be for the child also.


Example 1:


public class MyThread extends Thread
{
}
public class ThreadPriorityDemo
{
	public static void main(String[] args)
	{
		System.out.println(Thread.currentThread().getPriority());//5
		Thread.currentThread().setPriority(9);
		MyThread t=new MyThread();
		System.out.println(t.getPriority());//9
	}
}

Example 2:



public class MyThread extends Thread
{
	public void run()
	{
		for(int i=0;i<10;i++)
		{
			System.out.println("child thread");
		}
	}
}
public class ThreadPriorityDemo
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();
		//t.setPriority(10);	             1
		t.start();
		for(int i=0;i<10;i++)
		{
			System.out.println("main thread");
		}
	}
}


If we are commenting line 1 then both main and child Threads will have the same priority and hence we can’t expect exact execution order.
If we are not commenting line 1 then child Thread has the priority 10 and main Thread has the priority 5 hence child Thread will get chance for execution and after completing child Thread main Thread will get the chance in this the output is:


Output:

child thread
child thread
child thread
child thread
child thread
child thread
child thread
child thread
child thread
child thread
main thread
main thread
main thread
main thread
main thread
main thread
main thread
main thread
main thread
main thread


Some operating systems(like windowsXP) may not provide proper support for Thread priorities. We have to install separate bats provided by vendor to provide support for priorities.
The Methods to Prevent a Thread from Execution:


We can prevent(stop) a Thread execution by using the following methods.
1)    yield();
2)    join();
3)    sleep();

yield() method causes “to pause current executing Thread for giving the chance of remaining waiting Threads of same priority”.
If all waiting Threads have the low priority or if there is no waiting Threads then the same Thread will be continued its execution.
If several waiting Threads with same priority available then we can’t expect exact which Thread will get chance for execution.
The Thread which is yielded when it get chance once again for execution is depends on mercy of the Thread scheduler.
public static native void yield();
Diagram:


Example:

public class MyThread extends Thread
{
	public void run()
	{
		for(int i=0;i<5;i++)
		{
			Thread.yield();
			System.out.println("child thread");
		}
	}
}
public class ThreadYieldDemo
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();
		t.start();
		for(int i=0;i<5;i++)
		{
			System.out.println("main thread");
		}
	}
}


Output:

main thread
main thread
main thread
main thread
main thread
child thread
child thread
child thread
child thread
child thread


In the above example the chance of completing main Thread 1st is high because child Thread always calling yield() method.
Join():
If a Thread wants to wait until completing some other Thread then we should go for join() method.
Example:
If a Thread t1 executes t2.join() then t1 should go for waiting state until completing t2.


Diagram:



1)    public final void join()throws InterruptedException
2)    public final void join(long ms) throws InterruptedException
3)    public final void join(long ms,int ns) throws InterruptedException

Diagram:


Every join() method throws InterruptedException, which is checked exception hence compulsory we should handle either by try catch or by throws keyword.

Example:


public class MyThread extends Thread
{
	public void run()
	{
		for(int i=0;i<5;i++)
		{
			System.out.println("Seethe Thread");
			try
			{
				Thread.sleep(2000);
			}
			catch (InterruptedException e){}
		}
	}
}
public class ThreadJoinDemo
{
	public static void main(String[] args)throws InterruptedException
	{
		MyThread t=new MyThread();
		t.start();
		//t.join();	      1
		for(int i=0;i<5;i++)
		{
			System.out.println("Rama Thread");
		}
	}
}

If we are commenting line 1 then both Threads will be executed simultaneously and we can’t expect exact execution order.
If we are not commenting line 1 then main Thread will wait until completing child Thread in this the output is seethe Thread 5 times followed by Rama Thread 5 times.
Sleep() method:
If a Thread don’t want to perform any operation for a particular amount of time then we should go for

sleep() method.

1)    public static native void sleep(long ms) throws InterruptedException
2)    public static void sleep(long ms,int ns)throws InterruptedException

Diagram:

public class ThreadJoinDemo
{
	public static void main(String[] args)throws InterruptedException
	{
		System.out.println("M");
		Thread.sleep(3000);
		System.out.println("E");
		Thread.sleep(3000);
		System.out.println("G");
		Thread.sleep(3000);
		System.out.println("A");
	}
}

Output:
M
E
G
A


Interrupting a Thread:


We can interrupt a sleeping or waiting Thread by using interrupt()(break off) method of Thread class.


Example:




public class MyThread extends Thread
{
	public void run()
	{
		try
		{
			for(int i=0;i<5;i++)
			{
				System.out.println("i am lazy Thread :"+i);
				Thread.sleep(2000);
			}
		}
		catch (InterruptedException e)
		{
			System.out.println("i got interrupted");
		}
	}
}
public class ThreadInterruptDemo
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();
		t.start();
		//t.interrupt();		         1
		System.out.println("end of main thread");
	}
}

If we are commenting line 1 then main Thread won’t interrupt child Thread and hence child Thread will be continued until its completion.
If we are not commenting line 1 then main Thread interrupts child Thread and hence child Thread won’t continued until its completion in this case the output is:
End of main thread
I am lazy Thread: 0
I got interrupted


Note:

Whenever we are calling interrupt() method we may not see the effect immediately, if the target Thread is in sleeping or waiting state it will be interrupted immediately.
If the target Thread is not in sleeping or waiting state then interrupt call will wait until target Thread will enter into sleeping or waiting state. Once target Thread entered into sleeping or waiting state it will effect immediately.
In its lifetime if the target Thread never entered into sleeping or waiting state then there is no impact of interrupt call simply interrupt call will be wasted.


Example:

public class MyThread extends Thread
{
	public void run()
	{
		for(int i=0;i<5;i++)
		{
			System.out.println("iamlazy thread");
		}
		try
		{
			Thread.sleep(3000);
		}
		catch (InterruptedException e)
		{
			System.out.println("i got interrupted");
		}
	}
}
public class ThreadInterruptDemo1
{
	public static void main(String[] args)
	{
		MyThread t=new MyThread();
		t.start();
		t.interrupt();
		System.out.println("end of main thread");
	}
}

In the above program interrupt() method call invoked by main Thread will wait until child Thread entered into sleeping state.
Once child Thread entered into sleeping state then it will be interrupted immediately.


Synchronization


1)   Synchronized is the keyword applicable for methods and blocks but not for classes and variables.
2)   If a method or block declared as the synchronized then at a time only one Thread is allow to execute that method or block on the given object.
3)   The main advantage of synchronized keyword is we can resolve date inconsistency problems.
4)   But the main disadvantage of synchronized keyword is it increases waiting time of the Thread and effects performance of the system.
5)   Hence if there is no specific requirement then never recommended to use synchronized keyword.
Internally synchronization concept is implemented by using lock concept.
6)   Every object in java has a unique lock. Whenever we are using synchronized keyword then only lock concept will come into the picture.
7)   If a Thread wants to execute any synchronized method on the given object 1st it has to get the lock of that object. Once a Thread got the lock of that object then it’s allow to execute any synchronized method on that object. If the synchronized method execution completes then automatically Thread releases lock.
8)   While a Thread executing any synchronized method the remaining Threads are not allowed execute any synchronized method on that object simultaneously. But remaining Threads are allowed to execute any non-synchronized method simultaneously. [lock concept is implemented based on object but not based on method].


Example:


public class Display
{
	public synchronized void wish(String name)
	{
		for(int i=0;i<5;i++)
		{
			System.out.print("good morning:");
			try
			{
				Thread.sleep(1000);	
			}
			catch (InterruptedException e)
			{}
			System.out.println(name);
		}
	}
}
public class MyThread extends Thread
{
	Display d;
	String name;
	MyThread(Display d,String name)
	{
		this.d=d;
		this.name=name;
	}
	public void run()
	{
		d.wish(name);
	}
}
public class SynchronizedDemo
{
	public static void main(String[] args)
	{
		Display d1=new Display();
		MyThread t1=new MyThread(d1,"dhoni");
		MyThread t2=new MyThread(d1,"yuvaraj");
		t1.start();
		t2.start();
	}
}

If we are not declaring wish() method as synchronized then both Threads will be executed simultaneously and we will get irregular output.


Output:

good morning:good morning:yuvaraj
good morning:dhoni
good morning:yuvaraj
good morning:dhoni
good morning:yuvaraj
good morning:dhoni
good morning:yuvaraj
good morning:dhoni
good morning:yuvaraj


dhoni



If we declare wish()method as synchronized then the Threads will be executed one by one  that is until completing the 1st Thread the 2nd Thread will wait in this case we will get regular output which is nothing but


Output:

good morning:dhoni
good morning:dhoni
good morning:dhoni
good morning:dhoni
good morning:dhoni
good morning:yuvaraj
good morning:yuvaraj
good morning:yuvaraj
good morning:yuvaraj
good morning:yuvaraj


Case study:
Case 1:

Display d1=new Display();
Display d2=new Display();
MyThread t1=new MyThread(d1,"dhoni");
MyThread t2=new MyThread(d2,"yuvaraj");
t1.start();
t2.start();
Diagram:


Even though we declared wish() method as synchronized but we will get irregular output in this case, because both Threads are operating on different objects.


Class level lock:


1)    Every class in java has a unique lock. If a Thread wants to execute a static synchronized method then it required class level lock.
2)   Once a Thread got class level lock then it is allow to execute any static synchronized method of that class.
3)   While a Thread executing any static synchronized method the remaining Threads are not allow to execute any static synchronized method of that class simultaneously.
4)   But remaining Threads are allowed to execute normal synchronized methods, normal static methods, and normal instance methods simultaneously.
5)   Class level lock and object lock both are different and there is no relationship between these two.
Synchronized block:
6)   If very few lines of the code required synchronization then it’s never recommended to declare entire method as synchronized we have to enclose those few lines of the code with in synchronized block.
7)   The main advantage of synchronized block over synchronized method is it reduces waiting time of Thread and improves performance of the system.


Example 1: To get lock of current object we can declare synchronized block as follows.
Synchronized(this){}
Example 2: To get the lock of a particular object ‘b’ we have to declare a synchronized block as follows.
Synchronized(b){}
Example 3: To get class level lock we have to declare synchronized block as follows.
Synchronized(Display.class){}


As the argument to the synchronized block we can pass either object reference or “.class file” and we can’t pass primitive values as argument [because lock concept is dependent only for objects and classes but not for primitives].
Example:

Int x=b;
Synchronized(x){}

Output:

Compile time error.
Unexpected type.
Found: int
Required: reference


For inter thread communication please refer this