Thread Synchronization in Java
In Java, the smallest processing component is a thread, which is a small subprocess. It follows a different course of action.
Threads are autonomous. If an exception occurs in one thread, it does not affect the others. Moreover, it utilizes shared memory.
Thread Class in Java
To facilitate thread development, Java offers the Thread class. To generate and manage threads, the Thread class offers constructors and methods. Extending the Object class, the Thread class implements the Runnable interface.
Thread methods in Java
- start() - This function begins the thread's execution.
- Run() – This function executes a thread's command.
- sleep()- puts a thread to sleep for the provided period of time.
- currentThread()-returns a reference to the thread object that is currently running.
- Join () - It watches for a thread to terminate.
- getPriority() – This function returns the thread's priority.
- setPriority()-modifies the thread's priority.
- The thread's name is returned via the getName() function.
- setName()-modifies the thread's name.
- getId()- The Thread's id is returned via the getId() method.
- isAlive()- It determines whether the Thread is active.
- suspend()- The Thread is suspended using the suspend().
- resume()-The suspended Thread can be resumed using the resume() function.
- stop()-Use the stop() function to end the Thread.
- interrupt()-The Thread is interrupted by interrupt().
- isinterrupted()-The function isinterrupted() checks to see if the Thread has been halted.
- interrupted()- It checks to see if the running Thread has been paused.
- tostring()-The Thread's name, priority, and thread group are all included in the string representation of this Thread returned by the function toString().
- notify()- Use the notify() function to send a notification to just one Thread that is waiting for a specific item.
- notifyAll()- Use the notifyAll() function to send the notification to every waiting thread for a specific object.
Life Cycle of a Thread
Any one of the five states is possible for a thread. It is claimed that the Java thread life cycle has only four possible states: new, runnable, non-runnable, and terminated. No running state exists.
However, we are explaining it in the context of the five states to help with the threading.
JVM regulates a thread's life cycle when it is written in Java. These are the java thread states:
1) New
If you create an instance of the Thread class before calling the start() method, the Thread is in a new state.
2) Runnable (Operable)
After calling the start() method, the Thread is in a runnable state, but the thread scheduler has not chosen it to be the active Thread.
3)Running(Exercise)
If the thread scheduler has chosen it, the Thread is in the running state.
4) Non-Runnable (Blocked)
The Thread is still active in this state but is not currently permitted to execute.
5) Terminated (Discontinued)
When a thread's run() method exits, it is said to be in a terminated or dead state.
Thread Creation:
A thread can be made in one of two ways:
- Extending the thread class.
- By the implementation of the runnable interface.
The constructors and methods provided by the thread class allow users to create and manage threads. Extending the Object class, the Thread class implements the Runnable interface.
1. Extending the thread class example:
class Multithread extends Thread
{
public void run()
{
System.out.println("the thread is in the running state...");
}
public static void main(String args[])
{
Multithread thr = new Multithread();
thr.start();
}
}
Output:
the Thread is in the running state...
2. By the implementation of a runnable interface example:
class Multithread implements Runnable
{
public void run()
{
System.out.println("the thread is in the running state...");
}
public static void main(String args[])
{
Multithread mth = new Multithread();
Thread thr=new Thread(mth);
thr.start();
}
}
Output:
the Thread is in the running state...
Your class object will not be considered as a thread object if you are not extending the Thread class. Thus, you must explicitly create an instance of the Thread class. In order for the run() method of your class to function, we are supplying the object of your class that implements Runnable.
Sleep() method in Java
A thread can be put to sleep for a specified period of time using the sleep() function of the Thread class.
Example:
class Sleep extends Thread
{
public void run()
{
for(int i=1; i<6; i++)
{
try
{
Thread.sleep(600);
}
catch(InterruptedException ex)
{
System.out.println(ex);
}
System.out.println(i);
}
}
public static void main(String args[])
{
Sleep slt1 = new Sleep();
Sleep slt2 = new Sleep();
slt1.start();
slt2.start();
}
}
Output:
1
1
2
2
3
3
4
4
5
5
Thread Synchronization
Java's thread synchronization feature allows for the management of multiple threads' access to any shared resource.
Where we wish to allow only one Thread to access the shared resource, synchronization is a better alternative.
WHY…...?
The primary purpose of synchronization is to avoid consistency issues and prevent thread interference.
Thread synchronization can be divided into two categories: inter-thread communication and mutual exclusion.
- Again, the mutual exclusion is categorized into three categories
- Method of synchronization.
- Block of synchronization.
- Static synchronization.
Method of Synchronization:
Any method that has been declared as synchronized is referred to as method of synchronization.
An object is locked for any shared resource using a synchronized method.
A synchronized method call by a thread immediately gets the lock for that object and releases it after the method has finished running.
Example:
class Multiplication
{
synchronized void printMultiplication(int a) //method of synchronization
{
for(int i=1; i<=5; i++)
{
System.out.println(a*i);
try
{
Thread.sleep(500);
}
catch(Exception ex)
{
System.out.println(ex);
}
}
}
}
class Testthread1 extends Thread
{
Multiplication m;
Testthread1(Multiplication m)
{
this.m = m;
}
public void run()
{
m.printMultiplication(6);
}
}
class Testthread2 extends Thread
{
Multiplication m;
Testthread2(Multiplication m)
{
this.m = m;
}
public void run()
{
m.printMultiplication(50);
}
}
class Synchronization
{
public static void main(String args[])
{
Multiplication object = new Multiplication();
Testthread1 thr1 = new Testthread1(object);
Testthread2 thr2 = new Testthread2(object);
thr1.start();
thr2.start();
}
}
Output:
6
12
18
24
30
50
100
150
200
250
Block of Synchronization:
Any specific resource of the method can be synchronized using the synchronized block.
If your method contains 50 lines of code, but you want to synchronize 5 of them, you can use a synchronized block.
If you include all of the method's codes in the synchronized block, it will function similarly to the synchronous procedure.
Reminders for synchronized blocks
- To lock an object for any shared resource, use a synchronized block.
- The synchronized block's scope is less extensive than the methods.
Example: It is a block. And, we are not going to write the main method, and hence we will not get the output.
class Multiplication
{
void printMultiplcation(int a)
{
synchronized(this) //block of synchronization
{
for(int i=1; i<=5; i++)
{
System.out.println(a*i);
try
{
Thread.sleep(500);
}
catch(Exception ex)
{
System.out.println(ex);
}
}
}
}//end
}
Static synchronization:
If you synchronize any static method, the lock will be on the class rather than the object.
Example:
class Multiplication
{
synchronized static void printMultiplication(int a)
{
for(int i=1;i<=10;i++)
{
System.out.println(a*i);
try
{
Thread.sleep(500);
}
catch(Exception ex)
{
}
}
}
}
class Testthread1 extends Thread
{
public void run()
{
Multiplication.printMultiplication(10);
}
}
class Testthread2 extends Thread
{
public void run()
{
Multiplication.printMultiplication(10);
}
}
class Testthread3 extends Thread
{
public void run()
{
Multiplication.printMultiplication(100);
}
}
class Testthread4 extends Thread
{
public void run()
{
Multiplication.printMultiplication(1000);
}
}
class Synchronisation
{
public static void main(String args[])
{
Testthread1 thr1 = new Testthread1();
Testthread2 thr2 = new Testthread2();
Testthread3 thr3 = new Testthread3();
Testthread4 thr4 = new Testthread4();
thr1.start();
thr2.start();
thr3.start();
thr4.start();
}
}
Output:
10
20
30
40
50
60
70
80
90
100
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
10
20
30
40
50
60
70
80
90
100
100
200
300
400
500
600
700
800
900
1000
Inter-thread communication:
Having synchronized threads communicate with one another is what inter-thread communication or cooperation is all about.
Cooperation (Inter-Thread Communication) is a method that pauses a running thread. Another thread may enter (or lock) in the critical area of a thread's code without interfering with its critical section executed.
It is implemented via the following Object class methods:
- wait()
- notify()
- notifyAll()
Example:
class Employee
{
int cash=100000;
synchronized void withdraw(int cash)
{
System.out.println("we are going to withdraw a cash...");
if(this.cash<cash)
{
System.out.println("Low balance; waiting for the deposit of money...");
try
{
wait();
}
catch(Exception ex)
{
}
}
this.cash -= cash;
System.out.println("the withdrawal is completed...");
}
synchronized void deposit(int cash)
{
System.out.println("the deposit of money start...");
this.cash += cash;
System.out.println("the money deposited... ");
notify();
}
}
class Main
{
public static void main(String args[])
{
final Employee e = new Employee();
new Thread()
{
public void run()
{
e.withdraw(150000);
}
}.start();
new Thread()
{
public void run()
{
e.deposit(100000);
}
}.start();
}
}
Output:
we are going to withdraw a cash...
Low balance; waiting for the deposit of money...
the deposit of money start...
the money deposited...
the withdrawal is completed...