Thread in Java

4 mins read339 Views Comment
clickHere
Atul
Atul Harsha
Senior Manager Content
Updated on Jan 12, 2023 15:28 IST

Threads in Java allow for concurrent execution, improving the performance and responsiveness of programs. Learn how to create and use threads effectively.

2023_01_Thread-in-Java.jpg

A thread in Java is like a separate little task that runs alongside other tasks in a program. It is mainly used for parallel processing. You can think of it like having multiple people working on different things at the same time. Each person, or thread, has their own specific job to do and they can work independently of each other. This allows the program to perform multiple things at the same time and makes it run more efficiently.

  • In Java, a thread is a separate task that runs alongside other tasks in a program.
  • Each thread has its own specific job to do and can work independently of each other.
  • They are lightweight and don’t share memory or resources with each other.
  • Threads communicate with each other through shared objects and message passing.
  • Allows the program to get more things done at the same time and makes it run more efficiently.

Creating Threads in Java

There are two ways to create a thread in Java:

Method 1: Extending the Thread class:

  • Make a new class that “extends” the Thread class and put your task instructions in the “run” method for overriding it.
  • Use the “start” method to start your task by creating an example of the class.
 
class MyThread extends Thread {
public void run() {
// code to be executed in the new thread
}
}
//Running the thread
MyThread thread = new MyThread();
thread.start();
Copy code

Method 2: Implementing the Runnable interface:

  • Create a new class that implements the Runnable interface and define the run() method.
  • To start the thread, create an instance of the class and pass it to the constructor of the Thread class. Then call the start() method.
 
class MyRunnable implements Runnable {
public void run() {
// code to be executed in the new thread
}
}
//Running the thread
Thread thread = new Thread(new MyRunnable());
thread.start();
Copy code

Why use Thread in Java?

  • Increase program performance by utilizing concurrent execution using threads.
  • Allow for multitasking and handle multiple operations simultaneously.
  • Improve user experience by performing background tasks without freezing the user interface.
  • Make full use of multiple CPU cores for improved speed.
  • Facilitate communication and coordination between different tasks.

Concurrency Problem in Thread

When multiple threads access shared resources simultaneously, it can lead to problems called concurrency issues. Some examples of concurrency problems in threads include:

  • Race conditions: When two or more threads access shared data and try to change it at the same time, leading to unexpected results.
  • Deadlocks: Two or more threads are blocked waiting for each other to release resources they need, leading to a situation where none of the threads can proceed.
  • Livelock: Two or more threads are actively trying to acquire a resource but are unable to do so because they are constantly blocked by the other threads holding the resources.
  • Starvation: Occurs when a thread is unable to acquire the resources it needs to complete its task due to the fact that other threads are continuously using them.

Concurrency Problem based Scenario

Let’s say you are a software developer working for a bank. Your manager has asked you to develop a program that can transfer money between two bank accounts using multiple threads. Now the program is facing following challenges:

  • Able to handle multiple concurrent transactions.
  • Ensure that the overall balance remains correct and avoid any data inconsistencies like a race condition, deadlock or starvation.

Check out the below code to understand it better:

 
class Account {
private int balance;
public Account(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public synchronized void withdraw(int amount) {
balance -= amount;
}
public synchronized void deposit(int amount) {
balance += amount;
}
}
class Transaction extends Thread {
private Account fromAccount;
private Account toAccount;
private int amount;
public Transaction(Account fromAccount, Account toAccount, int amount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
public void run() {
// Get lock on the 'fromAccount'
synchronized (fromAccount) {
// Get lock on the 'toAccount'
synchronized (toAccount) {
// Withdraw from 'fromAccount'
fromAccount.withdraw(amount);
// Deposit to 'toAccount'
toAccount.deposit(amount);
}
}
}
}
public class DeadlockExample {
public static void main(String[] args) {
Account account1 = new Account(100);
Account account2 = new Account(200);
Transaction thread1 = new Transaction(account1, account2, 10);
Transaction thread2 = new Transaction(account2, account1, 20);
thread1.start();
thread2.start();
}
}
Copy code

In the above example,

  • Two classes, Account and Transaction are defined.
  • Account class have 2 methods: withdraw() and deposit() which are synchronized.
  • Transaction class extends Thread and has two Account objects, one for withdraw and another for deposit, and an amount for transaction.
  • In the run method of the Transaction class, it tries to acquire lock on the fromAccount first, then toAccount and does the transaction.
  • But in the main method, two Transaction threads are created, thread1 for transferring 10 units from account1 to account2 and thread2 for transferring 20 units from account2 to account1.
  • Now, if thread1 acquires lock on account1 first and thread2 acquires lock on account2 first, both threads will be waiting for each other to release the lock they are holding, leading to a Deadlock scenario, where the program will be stuck and not able to proceed further.

How to avoid Deadlock in this Scenario?

In the run method, a code is added to avoid deadlock by creating variables to hold the accounts and comparing the hashcode of the two accounts. The threads will always acquire the locks in the same order, regardless of the order in which the transactions were created.

Here is an example of how the deadlock situation can be avoided:

 
class Account {
private int balance;
// constructor to initialize balance
public Account(int balance) {
this.balance = balance;
}
// getter method for balance
public int getBalance() {
return balance;
}
// method to withdraw from the account
public void withdraw(int amount) {
balance -= amount;
}
// method to deposit to the account
public void deposit(int amount) {
balance += amount;
}
}
class Transaction extends Thread {
private Account fromAccount;
private Account toAccount;
private int amount;
// constructor to initialize the accounts and the amount
public Transaction(Account fromAccount, Account toAccount, int amount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
public void run() {
// create variables to hold the accounts
Account firstAccount, secondAccount;
// compare the hashcode of the two accounts
if(fromAccount.hashCode() > toAccount.hashCode()){
// assign the account with the lower hashcode to firstAccount
firstAccount = toAccount;
secondAccount = fromAccount;
}else{
// assign the account with the higher hashcode to firstAccount
firstAccount = fromAccount;
secondAccount = toAccount;
}
// Get lock on the 'firstAccount'
synchronized (firstAccount) {
// Get lock on the 'secondAccount'
synchronized (secondAccount) {
// Withdraw from 'fromAccount'
fromAccount.withdraw(amount);
// Deposit to 'toAccount'
toAccount.deposit(amount);
}
}
}
}
public class DeadlockExample {
public static void main(String[] args) {
// create two accounts
Account account1 = new Account(100);
Account account2 = new Account(200);
// create two transaction threads
Transaction thread1 = new Transaction(account1, account2, 10);
Transaction thread2 = new Transaction(account2, account1, 20);
// start the threads
thread1.start();
thread2.start();
}
}
Copy code

Read More:

Swapping of Two Numbers in Java
Swapping of Two Numbers in Java
Get an introduction to swapping the values of two numbers in Java using different methods. Compare the use of a temporary variable, arithmetic operators, and bitwise operators for swapping numbers.
How to Check Leap Year in Java?
How to Check Leap Year in Java?
In this blog, you will learn how to check leap year in Java. A leap year is a year that is divisible by 4 and 400 but not by 100....read more
Switch Case in Java with Examples
Switch Case in Java with Examples
Switch statements in Java enable execution of different code blocks based on the value of an expression. They provide an alternative to multiple if-else statements and can be used when...read more
Swapping of Two Numbers in Java
Swapping of Two Numbers in Java
Get an introduction to swapping the values of two numbers in Java using different methods. Compare the use of a temporary variable, arithmetic operators, and bitwise operators for swapping numbers.
How to Check Leap Year in Java?
How to Check Leap Year in Java?
In this blog, you will learn how to check leap year in Java. A leap year is a year that is divisible by 4 and 400 but not by 100....read more
Switch Case in Java with Examples
Switch Case in Java with Examples
Switch statements in Java enable execution of different code blocks based on the value of an expression. They provide an alternative to multiple if-else statements and can be used when...read more
Armstrong Number in Java using Different Methods
Armstrong Number in Java using Different Methods
Learn to check if a number is an Armstrong number in Java using while loop or recursion. Understand the mathematical definition and see code examples for easy implementation
Check Palindrome in Java Using Different Methods
Check Palindrome in Java Using Different Methods
Discover various methods for checking if a string is a palindrome in Java. Use recursion and string manipulation to efficiently determine if a string is a palindrome, ignoring spaces, punctuation,...read more
Fibonacci Series in Java [Case-Study Based]
Fibonacci Series in Java [Case-Study Based]
The Fibonacci series is a sequence of numbers in which each number is the sum of the two preceding ones, usually starting with 0 and 1. In Java, the Fibonacci...read more
About the Author
author-image
Atul Harsha
Senior Manager Content

Experienced AI and Machine Learning content creator with a passion for using data to solve real-world challenges. I specialize in Python, SQL, NLP, and Data Visualization. My goal is to make data science engaging an... Read Full Bio