Java Threads and its methods with example

Daily Debug
Javarevisited
Published in
9 min readDec 29, 2023

Introduction

In this article we are going to discuss basics of thread, how thread is created in java and a few important methods of thread.

This article will help you prepare for Java threads interview questions as well as it includes a lot of coding examples.

What is Thread ?

Thread is the smallest unit of processes which executes independently within the program, JVM allows an application multiple threads of execution concurrently.

How can we create thread in java ?

There are 2 methods we can create threads in Java.

By extending thread class

public class ThreadExample extends Thread {
public static void main(String args[]) {
ThreadExample threadExample = new ThreadExample();
threadExample.start();
}
public void run() {
System.out.println(Thread.currentThread().getName() + "Started");
}
}

By implementing runnable Interface.

public class ThreadExample implements Runnable {
public static void main(String args[]) {
ThreadExample threadExample = new ThreadExample();
Thread thread = new Thread(threadExample);
thread.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "Started");
}
}

Methods of Thread -

activeCount()

Returns an active thread count of the current threads thread group and its subgroup.

public class ThreadMethods extends Thread{
public static void main(String args[]){
System.out.println("Current active count " + activeCount());
}
}
// output
Current active count 2

currentThread()-

Refers to the currently running thread object.

public class ThreadMethods extends Thread{
public static void main(String args[]){
System.out.println("Current thread running " + currentThread());
}
}

//output
Current thread running Thread[main,5,main]

getId()

Returns the identifier of the thread.

public class ThreadMethods extends Thread{
public static void main(String args[]){
System.out.println("ID of the current thread - " + currentThread().getId());
}
}

// output
Current thread's id 1

getName() -

Returns the current thread’s name.

setName(String name)-

Assigns a new name to the thread.


public class ThreadMethods extends Thread{
public static void main(String args[]){
System.out.println("Current thread's old name " + currentThread().getName());
currentThread().setName("New Name");
System.out.println("Current thread's new name " + currentThread().getName());
}
}
//output
Current thread's old name main
Current thread's new name New Name

setPriority(int newPriority)

Sets new priority to the thread.

getPriority()

Returns the thread’s priority.

public class ThreadMethods extends Thread{
public static void main(String args[]){
System.out.println("Current thread's old priority " + currentThread().getPriority());
currentThread().setPriority(2);
System.out.println("Current thread's new priority " + currentThread().getPriority());
}
}

//output
Current thread's old priority 5
Current thread's new priority 2

run() -

When the run method is called it simply executes the code in the run method same as the normal method.

The run method simply executes the code specified in the run method, it does not create a new thread, in the below example we printed the current thread in the run method which will return as main, even though we created a new thread ThreadMethods.

public class ThreadMethods extends Thread{
@Override
public void run() {
System.out.println("run method called ");
System.out.println("Current Thread running " + currentThread().getName());
}

public static void main(String args[]){
ThreadMethods threadMethods = new ThreadMethods();
threadMethods.run();
}
}
//output
run method called
Current Thread running main

start()

Creates a new thread and starts the execution of code in the run method.

We will only change the run method call to the start method call in the above example and observe the output-

public class ThreadMethods extends Thread{
@Override
public void run() {
System.out.println("run method called ");
System.out.println("Current Thread running " + currentThread().getName());
}

public static void main(String args[]){
ThreadMethods threadMethods = new ThreadMethods();
threadMethods.start();
}
}

// output
run method called
Current Thread running Thread-0

getThreadGroup()

Returns the current thread group this thread belongs to.

public class ThreadMethods extends Thread{
@Override
public void run() {
System.out.println(currentThread().getThreadGroup());
}

public static void main(String args[]){
ThreadMethods threadMethods = new ThreadMethods();
threadMethods.start();
}
}

//output
java.lang.ThreadGroup[name=main,maxpri=10]

interrupt()

Interrupts this thread.

interrupted()

Tests whether this thread is interrupted.

class ThreadMethods extends Thread {
@Override
public void run() {
System.out.println("Thread Started");
try {
for (int i = 0; i < 5; i++) {
System.out.println("Child thread executing");
sleep(1000);
}
System.out.println("Thread executed");
} catch (InterruptedException e) {

e.printStackTrace();
}
}

}

public class ThreadExecution {

public static void main(String args[]) throws InterruptedException {

ThreadMethods threadMethods = new ThreadMethods();
threadMethods.start();
while (!threadMethods.isInterrupted()) {
System.out.println("not interrupted ");
sleep(2000);
//Main thread interrupting child thread
threadMethods.interrupt();
}

System.out.println("Main thread execution completed");

}
}
/* OUTPUT

not interrupted
Thread Started
Child thread executing
Child thread executing
Main thread execution completed
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at ThreadMethods.ThreadMethods.run(ThreadExecution.java:13)

Process finished with exit code 0
*/

isAlive()

Tests whether this thread is alive.

class ThreadMethods extends Thread {
@Override
public void run() {
System.out.println("Thread Started");
try {
for (int i = 0; i < 5; i++) {
System.out.println("Child thread executing");
sleep(1000);
}
System.out.println("Thread executed");
} catch (InterruptedException e) {

e.printStackTrace();
}
}

}

public class ThreadExecution {

public static void main(String args[]) throws InterruptedException {
ThreadMethods threadMethods = new ThreadMethods();
threadMethods.start();
while (threadMethods.isAlive()) {
System.out.println("Thread is alive - " + threadMethods.isAlive());
sleep(2000);
}
System.out.println("Thread terminated- " + threadMethods.isAlive());
}
}

/* OUTPUT

Thread Started
Child thread executing
Thread is alive - true
Child thread executing
Child thread executing
Thread is alive - true
Child thread executing
Child thread executing
Thread is alive - true
Thread executed
Thread terminated- false

/*

isDaemon()-

Tests whether this thread is a daemon thread or not.

setDaemon(boolean on)- Set if thread is daemon or not

public class DaemonThreadExample extends Thread {
String s;
public DaemonThreadExample(String name){
s=name;
}

public void run()
{
if(Thread.currentThread().isDaemon())
{
System.out.println(s + " is Daemon Thread");
}

else
{
System.out.println(s + " is User Thread");
}
}

public static void main(String[] args)
{

DaemonThreadExample thread1 = new DaemonThreadExample("thread1");
DaemonThreadExample thread2 = new DaemonThreadExample("thread2");
DaemonThreadExample thread3 = new DaemonThreadExample("thread3");

thread1.setDaemon(true);
thread1.start();
thread2.start();
thread3.setDaemon(true);
thread3.start();
}
}

join() -

Waits for the current thread to die before starting execution of the thread on which the join method is called.

class ThreadMethods extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 3; i++) {
System.out.println("current thread " + currentThread().getName());
sleep(1000);
}
System.out.println("Thread executed");
} catch (InterruptedException e) {

e.printStackTrace();
}
}

}

public class ThreadExecution {

public static void main(String args[]) throws InterruptedException {
ThreadMethods thread1 = new ThreadMethods();
ThreadMethods thread2 = new ThreadMethods();
thread1.start();
thread1.join(); // This will make next thread to wait until this thread completes its execution
thread2.start();
}
}

/* OUTPUT with JOIN Method
current thread Thread-0
current thread Thread-0
current thread Thread-0
Thread executed
current thread Thread-1
current thread Thread-1
current thread Thread-1
Thread executed

OUTPUT if you comment the join method and run the above program

current thread Thread-0
current thread Thread-1
current thread Thread-0
current thread Thread-1
current thread Thread-1
current thread Thread-0
Thread executed
Thread executed

*/

yield()

A hint to the scheduler that the current thread is willing to yield its current use of the processor.

Yield tells the processor that it is ready to give up its resources voluntarily, this method is platform-dependent and its behaviour is not predictable.

I ran the following program twice and it gave different results each time.

class ThreadMethods extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("current thread " + currentThread().getName());
Thread.yield();
}
System.out.println("Thread executed");
}

}

public class ThreadExecution {

public static void main(String args[]) throws InterruptedException {
ThreadMethods thread1 = new ThreadMethods();
ThreadMethods thread2 = new ThreadMethods();
thread1.start();
// thread1.join(); // This will make next thread to wait until this thread completes its execution
thread2.start();
}
}

/*
---------------output 1------------------
current thread Thread-1
current thread Thread-1
current thread Thread-1
current thread Thread-0
Thread executed
current thread Thread-0
current thread Thread-0
Thread executed
-----------------------------output 2---------------------------------
current thread Thread-0
current thread Thread-1
current thread Thread-1
current thread Thread-0
current thread Thread-1
Thread executed
current thread Thread-0
Thread executed
*/

There are few methods in the Thread which are depreciated like Thread.stop(), Thread.suspend(),Thread.resume().

Why Thread.stop(), Thread.suspend(),Thread.resume() are depricated ?

Thread.stop() -

  • The stop method is used to terminate the thread execution, Stopping threads causes unlock all the monitors that it has locked.
  • It’s unsafe to call the stop method because it abruptly stops the execution of the thread.
  • If the thread is holding a lock on shared resources after stopping it, another thread will be able to use the resources which might be in an inconsistent state which can cause subtle behavior of the code which will be difficult to detect.
  • In the below example ThreadOne is waiting for notify method to be called from another thread, ThreadTwo is calling notify method, but before notify method call we are sstopping the execution of thread using stop() method, which stops the program to execute next statements.
  • Notify method in ThreadTwo will never get called and threadOne will wait forever which can cause deadlock in the program.
package ThreadMethods;


import static java.lang.Thread.sleep;

class Message {
String msg = "shared resource";

public Message(String msg) {
this.msg = msg;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}
}

class ThreadOne extends Thread {
Message msg;

public ThreadOne(Message msg) {
this.msg = msg;
}

@Override
public void run() {
System.out.println("Thread started running - " + currentThread().getName());
synchronized (msg) {
try {
msg.setMsg("Message from thread 1");
System.out.println("Before Notify- " + currentThread().getName() + " Current Message-- " + msg.getMsg());
msg.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("After Notify- " + currentThread().getName() + " Current Message --" + msg.getMsg());

}
}

class ThreadTwo extends Thread {
Message msg;

public ThreadTwo(Message msg) {
this.msg = msg;
}

@Override
public void run() {
System.out.println("Thread started running - " + currentThread().getName());
synchronized (msg) {
msg.setMsg("Message from thread 2");
currentThread().stop();
msg.notify();
}
System.out.println("Thread 2 executed");
}
}


public class ThreadExample {
public static void main(String args[]) throws InterruptedException {
Message message = new Message("Sharing msg object");
ThreadOne threadOne = new ThreadOne(message);
ThreadTwo threadTwo = new ThreadTwo(message);
threadOne.setName("Thread One");
threadTwo.setName("Thread Two");
threadOne.start();
sleep(500);
threadTwo.start();
}
}

/* OUTPUT with stop method
Thread started running - Thread One
Before Notify- Thread One Current Message-- Message from thread 1
Thread started running - Thread Two

OUTPUT WITHOUT STOP METHOD

Thread started running - Thread One
Before Notify- Thread One Current Message-- Message from thread 1
Thread started running - Thread Two
After Notify- Thread One Current Message --Message from thread 2
Thread 2 executed

*/

Thread.suspend() & Thread.resume()

  • This will put the thread execution on pause and the Thread will go into a waiting state until it resumes its execution. The thread. resume() method is used to resume the suspended thread.
  • Suspend and resume methods are also depreciated because these methods are deadlock-prone and can result in unexpected behaviour.
  • In the below program, we suspended threadOne and resumed it after notify was called, which caused notify method to call first and then the wait method, which resulted in infinite waiting of thread one and deadlock.
package ThreadMethods;


import static java.lang.Thread.sleep;

class Message {
String msg = "shared resource";

public Message(String msg) {
this.msg = msg;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}
}

class ThreadOne extends Thread {
Message msg;

public ThreadOne(Message msg) {
this.msg = msg;
}

@Override
public void run() {
System.out.println("Thread started running - " + currentThread().getName());
synchronized (msg) {
try {
msg.setMsg("Message from thread 1");
currentThread().suspend();
System.out.println("Thread one resumed, wait called ");
msg.wait();

} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("Thread one executed");

}
}

class ThreadTwo extends Thread {
Message msg;

public ThreadTwo(Message msg) {
this.msg = msg;
}

@Override
public void run() {
System.out.println("Thread started running - " + currentThread().getName());
synchronized (msg) {
msg.setMsg("Message from thread 2");
msg.notify();
}
System.out.println("Thread 2 executed, Notify called");

}
}


public class ThreadExample {
public static void main(String args[]) throws InterruptedException {
Message message = new Message("Sharing msg object");
ThreadOne threadOne = new ThreadOne(message);
ThreadTwo threadTwo = new ThreadTwo(message);
threadOne.setName("Thread One");
threadTwo.setName("Thread Two");
threadOne.start();
threadTwo.start();
while(threadOne.isAlive()){
sleep(5000);
System.out.println(threadOne.getState());
threadOne.resume();
}
}
}

/* Output

Thread started running - Thread Two
Thread 2 executed, Notify called
Thread started running - Thread One
RUNNABLE
Thread one resumed, wait called
WAITING
WAITING


*/
//Thread one went into infinite waiting - causes deadlock

I encourage you to try these all examples on your systems and observe the thread execution, these examples will help you get a better understanding of overall thread functionality and its method.

If you like the article please follow and subscribe, it encourages me to write more such articles.

Any feedback or doubts please drop them into the comments sections below.

--

--

Daily Debug
Javarevisited

I am software engineer, Tech blogger, here to help you learn and share my insights on various tech topics.