How Multithreading works in Java

Federico Haag
5 min readJan 1, 2018

Contents of the Article:

  1. Handling Threads in Java
  2. How to manage asynchrony in Java
  3. Advanced Handling of Concurrency

1. Handling Threads in Java

  • Each thread created/executed by a Java program is represented in the Java language through an instance of the class “Thread”.
  • A thread executes the code that has received on instantiation time through an instance of the class “Runnable”.
  • A thread starts executing itself after the invocation of its method .start()

If you do not need to use multiple times the same runnable in different threads, you could also do as the following example:

Someone is used to not using the runnable class but creating a subclass of Thread, changing the code into the method run() as follows:

I personally do not recommend doing it. Remember that inheritance’aim is to override some behavior, and this is not the case.

Remember that each java program you execute is always spawned in a main thread, even if you do not explicitly implement it. To get the reference to that main thread, use the static method of Thread class “currentThread()”.

📋 Threads Methods you’ll need the most:

  • start() causes the thread to begin execution; the Java Virtual Machine calls the run method of this thread
  • run() if the thread was constructed using a separate Runnable run object, then that Runnable object's run() method is called; otherwise, this method does nothing and returns (unless you changed the run method of Threadclass, as advised before to do not do)
  • interrupt(): interrupts the thread; when this method is called on a thread, an InterruptedException is thrown if it was waiting due to methods like wait(), join(), sleep()). Remember that interrupting does not mean killing the thread, but just sending an “interrupt signal” to the running code. What will happen depends on the implementation of — so — in particular ifInterruptedException is caught.
  • interrupted(): tests whether the thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it).
  • isInterrupted(): same behaviour of interrupted() but the interrupted status of the thread is not affected
  • yield():gives a hint to the scheduler that the current thread is willing to yield its current use of the processor. Use this function into your CPU-intensive code to prevent inconsistencies in operating systems that are not preemptive (more info here about preemptive OS)
  • join(): waits for the thread to die putting in pause the thread that has executed the join() method and restart it when the target thread is dead.
  • setPriority(): changes the priority of the thread (more info)
  • getState(): returns the state of the thread (do not use for synchronization purpose)
Java, The Complete Reference, Ninth Edition — Herbert Schildt

Be reasonable when dispatching threads: too many threads could induce CPU to spend more time in changing context than actual code execution!

2. How to manage asynchrony in Java

Synchronized keyword

Multithreading causes code to be executed asynchronously and this could create some inconsistencies. To avoid malicious behaviours sometimes is needed to force code to be executed in a synchronous way: this means that other threads that want to access the same data structure or call the same method have to wait for each other.

How to force different threads to run synchronously:

  1. Define a Monitor for the code you want to be executed synchronously

A monitor is an object that is used as a mutually exclusive lock. Once a thread has locked a monitor, others threads competing for the same monitor have to wait for the lock to be released.

Each Java Object (so each instance of any class) has its own monitor.

2. Lock the desired code using the synchronized keyword. There are two options how you can do this. Have a look at the following examples:

  • Calling a synchronized method: the monitor being locked is the specific instance of the class where you are calling the method.
  • Putting a code in a synchronized section: the monitor being locked is the object passed as parameter into the synchronized block

Use synchronized classes/method/sequences only when strictly needed because too much synchrony could induce an execution time too long.

Threads direct communication

  • wait(): the thread goes sleeping and wait that someone else wakes it up. Remember that wait() method can throw an InterruptedException so it’s always better to be prepaired to handle it.
  • notify(): wakes up one of the threads that previously called wait() from the same object from which it’s being now called notify().
  • notifyAll(): same asnotify() but wakes up all the waiting threads

These methods are available for each Object in Java. So writing myObject.wait() is always legitimate any type of object it is.

If these methods are called without an explicit object like the following example, this means that the object to be considered as called is the one where the call happened.

Warning: sometimes can happen that a thread is waked up by the operating system even if no notify has been called, as a result it’s better always to check the waking-up-condition before definitely starting the execution of the waked up thread (and eventually put it again in wait).

Remember that civility was born up to communication!

Problems with JVM Cache — Important!

The Java Virtual Machine is used to cache objects instances for each thread and it updates them without a strict approach. This can induce some inconsistencies: a method that reads a variable of an object, could not read the last version of it but its cached version.

For this reason, even read-only applications need some caution ⚠️

There are two ways to force JVM to update the cache values:

  • executing the read operation into a synchronized sequence (when a monitor is locked JVM force update of the thread cache)
  • declaring as volatile the variable (volatile forces JVM to use the values in the main memory, not from cache)

3. Advanced Handling of Concurrency

For advanced handling of concurrency in Java, is better to refer to the official reference of the Concurrency Utilities offered natively: java.util.concurrent

The previous explanation is intended as an introduction of Concurrency in Java. If you have to develop a real concurrent application, it’s better to learn first what is offered by java.util.concurrent and then, maybe, a complete professional Framework.