Multi-Threading in Java
Thread: Lightweight subprocess, with a separate and independent path of execution, created by java.lang.Thread
class.
Note: Instructions in a single thread always run synchronously.
States of a Thread
- NEW: Thread created but not yet started.
- RUNNABLE: Thread ready to run is moved in runnable state. Here, a thread is either running or is ready to run in JVM, though it may be waiting for other resources from the operating system such as processor.
- BLOCKED: Thread temporarily inactive as it tries to access a protected section of code that is currently locked by some other thread.
- WAITING: Thread temporarily inactive as its waiting for another thread to perform a particular action. Caused by calling:
1. Object.wait with no timeout
2. Thread.join with no timeout
3. LockSupport.park - TIMED_WAITING: Thread waiting for a specified time. Caused by:
1. Thread.sleep
2. Object.wait with timeout
3. Thread.join with timeout
4. LockSupport.parkNanos
5. LockSupport.parkUntil - TERMINATED: A thread that has exited is in this state.
Creating Threads
All threads are created cascading from the main
thread. Threads are created as:
- If the main thread has some long-running task, then as a thread is synchronous, this task will make the application appear non-responding. So, long-running independent tasks (like downloading files) must be run on separate threads.
- Multithreading allows concurrency and can increase performance by using multiple CPU cores.
Method 1: Using Thread Class
- How to use?
1. Create classMyThread
, and extend classThread
.
2. Override methodrun()
.
3. Create object ofMyThread
class and invoke methodstart()
which starts running code present in methodrun()
in a new thread. - Difference between
start()
andrun()
:
1.start()
creates a new thread and runs the code inrun()
in that, but if you callrun()
directly, the code in it will be executed in the same thread.
2. You can callstart()
on an thread instance once only (as it changes the state of thread), thoughrun()
can be called any number of times.
NOTE: Understand that CPU can switch between threads at any instance. Run this code multiple times and observe.
Method 2: Using Runnable Interface:
This is required and preferred as multiple-inheritance is not allowed in java.
Note: Anyhow, you need to use Thread
class only to create and execute a new thread. Constructors of class Thread
can have either, none or all of the following parameters:
- runnable_object
- thread_name (String)
Runnable reduces lines-of-code:
- Use anonymous class while passing object of
Runnable
inThread
constructor. - Use lambdas, as Runnable is a functional interface.
How to use?
- Create class
MyRunnable
, implement interfaceRunnable
. - override method
run()
- Create object of
Thread
by passing instance ofMyRunnable
.Thread thread = new Thread(myRunnable);
- Execute thread by
thread.start()
Examples for Syntax
Thread using class Thread
Take note of line 9, 30.
Thread using interface Runnable
- Without using anonymous class and lambdas:
- Using anonymous class and lambdas:
Daemon Threads
Daemon thread is the lowest priority thread that runs in the background to perform tasks such as garbage collection. Daemon threads cannot prevent JVM to exit once all users have completed execution (which means once all user threads are executed, JVM exits, closing all daemons threads also, even if they are running)
Syntax:
Because of line 10, nothing will print (as main exits as soon as t starts). Check this out.
Some Important Methods
Thread.currentThread() :
This static method return object of currently executing thread. Helpful in static context and where current class does not extend Thread
.
NAME and ID related methods:
Note: You can never set id for any thread.
- Using constructor: In the constructor of the class
Thread
, one may pass thread name as parameter. If you useMyThread extends Thread
usesuper
. See [ThreadUsingThreadClass] example. - Using getters and setters:
- Can usethread.getName()
,thread.getId()
to get the respective.
- Can usethis
asthread
object inside aThread
class.
- You may get thethread
object usingThread.currentThread()
. Helpful to getthread
object in static functions.
- Can usethread.setName("<name>")
for changing/setting name of a thread.
Thread.sleep(millisecs):
This method is used to make the current thread sleep for some milliseconds.
thread.isAlive() & thread.join()
A thread is alive if it has been started and has not yet died.
A thread “starts” when its start() method is invoked and “dies” at the end of its run() method. So yes, a thread is “alive” when its run() method is still ongoing, but it is also “alive” in the time window between the invocation of start() and the implicit invocation of the run() method by the JVM.
Usage:
Output:
false
true
true
false
Output if you comment m.join()
:
false
true
true
true
Thread Priorities
In JVM every thread must have a priority either given by default by JVM or by the programmer. Thread priorities in java vary from 1 to 10 (priority increases with the number).
- 1 = least priority
- 5 = normal priority
- 10 = highest priority
Default Priority: Child thread has the same priority as parent thread by default. The main thread has priority 5 by default.
Meaning of priority: Thread with the highest priority will get execution chance prior to other threads. If two have the same priority then we can’t expect which thread will execute first (depends on scheduler’s algo).
Inbuilt constants: Thread.MIN_PRRIORITY
, Thread.MAX_PRIORITY
, Thread.NORM_PRIORITY
.
Methods: thread.setPriority(int p)
, thread.getPriority()
.
Synchronized Keyword
In multithreading, multiple threads may try to access the same resources and finally produce erroneous results.
Synchronized Blocks (marked by keyword synchronized): Help in synchronization of tasks of different threads. A synchronized block in Java is synchronized on some object. All synchronized blocks synchronized on the same object can only have one thread executing inside them at a time.
In Java, synchronization is implemented using a concept called monitors. Only one thread can own a monitor at a given time.
Try to run the following code multiple times, you will find different results every time (as line 5 is not a single instruction and thread may change in between).
Solution: Add keyword synchronized
to method increament()
.
Inter-Thread Communication
Communication between two threads is done basically using methods: wait()
, notify()
, notifyAll()
methods.
Inter-thread communication is required when there is a dependence between two threads to perform a task which arises basically due to order in which parts of the task are to be done. This can be done using Polling (checking condition repeatedly).
A better approach can be to:
- Make the methods of the object which will make a thread
wait
ornotify
as synchronized. - If a thread A is dependent on some update from B, then:
- Make them work on the same object
- Make thread Await()
.
- Make change from thread B andnotify()
NOTE: Methods wait()
, notify()
, notifyAll()
are present in class Object
and not in class Thread
.
Thread Lifecycle
Difference b/w notify() and notifyAll()
notify()
method send notification to only one of waiting thread whilenotifyAll()
informs all threads waiting on that lock.- Using
notify()
, it’s not guaranteed that, which thread will be informed. notifyAll()
can drain more CPU cycles thannotify()
itself but if you really want to sure that your notification doesn’t get wasted by any reason, usenotifyAll()
.
Example: Producer-Consumer Problem
Desired: We want that as in a loop a sequence of putting a new value and getting it occurs.
Consider the program:
You will notice patterns like:
Put: 1
Put: 2
Get: 1
So we are getting the wrong value and the methods are not in order. Wrong value issue can be used by using the synchronized keyword for put()
and get()
. But, then the correct order may still not appear (try making the waiting times for while loops different to make the order problematic).
Correct way:
Here we have updated the put()
and get()
methods.
Notice:
- Multiple threads that are communicating using
wait()
andnotify()
must run on the same instance of the classObject
. - The
wait()
,notify()
,notify()
are all part of classObject
so must be present in the functions of the shared object.
- Note: Such functions must be synchronized. - In the above code, you can even change the sleep time for producer and consumer by any amount (bringing any difference), still, it won’t cause any problem.
- Try to notice the difference between
sleep
andwait
.
-sleep
is time-based, butwait
makes thread wait for notification.
-sleep
is part ofThread
class butwait
is part ofObject
class.