Thread in Java
Threads in Java allow for concurrent execution, improving the performance and responsiveness of programs. Learn how to create and use threads effectively.
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 threadMyThread thread = new MyThread();thread.start();
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 threadThread thread = new Thread(new MyRunnable());thread.start();
Best-suited Java courses for you
Learn Java with these high-rated online courses
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(); }}
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(); }}
Read More:
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