Threading in JAVA

Introduction

Threads allow developers to run multiple simultaneous tasks instead of one after another.

JAVA classes for Threading

The main classes used for threading in JAVA are the Thread class and the Runnable interface.
Note that the Thread class implements the Runnable interface.

  • The Thread class represents a thread to be executed.
  • The Runnable interface represents a task to run in a Thread.

The Runnable task

The Runnable interface have one “Run” method to define what this task will achieve.

public class MyRunnable implements Runnable {

    /**
    * The run method from the Runnable interface.
    * Defines what this runnable will achieve we ran
    * in a Thread.
    */
    @Override
    public void run() {
        while (true) {
            System.out.println("Test");
            // Wait for 1000ms (1 second) to avoid flooding the console.
            Thread.sleep(1000);
    }
}

The Thread

The Thread constructor can take a Runnable in parameter in order to specify what will happen when the thread is started.
Once created we need to call the “Start()” method to start the thread.

// Creating the runnable
MyRunnable myRun = new MyRunnable();
// Creating the thread
Thread myThread = new Thread(myRun);
// Starting the thread
myThread.start();

What if I use the Thread constructor without any parameter? Will my thread do nothing?
As specified earlier, The Thread class implements the Runnable Interface so you can also make a custom Thread class which won’t need a Runnable to start.

A Custom Thread class:

public class CustomThread extends Thread{
    @Override
    public void run() {
        // What to do when the thread is started
    }
}

Starting the custom Thread:

// Creating the custom thread
CustomThread customThread = new CustomThread();
// Starting the thread 
// Careful: if you use the Run() method directly
// the task won't run in another thread!
customThread.start();

So why bother with a Runnable if we can do without? Two main reasons:

  • Separation of concerns: Not mixing the thread management part and tasks processed will make the project easier to create, understand and maintain.
  • Inheritance: Implementing the Runnable interface let your class extends from another class which wouldn’t be possible if already extending the Thread class.

Thread methods

Once you have a thread up and running, you still have some possibilities in order to interact with it.

Sleep

When a thread is put to sleep it will totally stop working for a certain amount of time (in milliseconds).
The sleep operation throws InterruptedException that should be managed in a Try-Catch block.

 // Putting the current Thread to sleep for 1sec
 Thread.sleep(1000);

Yield

The yield method will pause momentarily the current thread to allow another thread that is ready to run to be processed by the processor.
This allow a thread not to overflow a processor when working on a resource-heavy task.

// Yielding the current Thread
Thread.yield(); 

Join

The join method all to pause the current thread until the specified thread finished its execution.

// Creating and running a thread
MyRunnable myRun = new MyRunnable();
Thread myThread = new Thread(myRun);
myThread.start();

// Pausing the current thread until
// myThread finished its execution.
myThread.join();

Interrupt

The interrupt method will ask to a thread to stop whenever possible. The interruption have to be managed on the Run method as you can’t stop a thread directly which would be too unsafe depending on the operation(s) in progress.

Two methods in order to manage a thread interruption in a Runnable:

  • The interrupted method

     while(!Thread.interrupted())
     {
         // As long as the thread isn't interupted
         // Do something...
     }
    
  • The InterruptionException

     try {
         while (true) {
             // Do Something...  
         } catch (InterruptedException e) {
             // When interrupted...
             System.out.println("Thread stopping...");
         }
     }
    

Synchronization

Working with multiple threads means some of them might try to interact with the same object or resources. When that happens some errors or unexpected behaviors often happens if the code is not thread-safe.

To avoid this we use synchronization which will basically forbid multiple threads to access the same part of code, method, or object at the same time.

The ReentrantLock class

The ReentrantLock class allows to lock and unlock at any place in the code.

private static ReentrantLock lock = new ReentrantLock();

public static void LockExample()
{
    lock.lock();
    // Do something...
    // This code will be executed
    // by only one thread at a time.
    lock.unlock();
}

Synchronized keyword

The Synchronized keyword allows to lock a whole method or an object.

// Synchronized method
public static synchronized void LockExample()
{
    // Do something...
    // This code will be executed
    // by only one thread at a time.
}

Synchronization on an object:

Object obj = new Object();

synchronized (obj) {
    // Do something...
    // This code will be run
    // by only one thread at a time.
}

Wait method

The wait method allows to pause the thread and release the lock, which means it have to be used in a synchronized block of code.
The wait method manage interruption like other methods and therefor requires to manage InterruptedException.

public synchronized void LockExample() {
    try {
        // Do something...

        // Wait: release the lock
        wait();
    } catch (InterruptedException e) {
        // What to do when interrupted
        e.printStackTrace();
    }
}

To restart a waiting thread you have to call its notify method. It’s also possible to resume all threads related to one object by using the notifyAll method.

// Waking up a thread
myThread.notify();

// Waking up all threads related to an object
myObj.notifyAll();