How Multithreading works in Java

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 istance of the class “Thread”.
  • A thread executes the code that has received on instantiation time through an istance 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 minify the code as following:

Someone is used to not using the runnuble class but creating a subclass of Thread, changing the code into the method run(). 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 spawn 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(): iterrupts 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, what will happens depends on the implementation of (so if you created the thread using a runnable instance as adviced).
  • 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 much 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 mean that other threads that want to access the same data structure or call the same method have to wait each others.

Key to synchronization is the concept of the monitor. A monitor is an object that is used as a mutually exclusive lock. Once a thread has locked a monitor, others threads competing for that monitor have to wait its unlocking.

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

You can lock&unlock the monitor as following:

  • calling a synchronized method: you lock the monitor of the object of the called method
  • calling any method of a synchronized object: you lock the monitor of the called method
  • entering a code of a synchronized section: you lock the monitor of 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 XXX.

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

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 strictly 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 syncronized 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 Concurency 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.

If you liked, please let me know it clapping 👏🏻 the article. Thank you 😊

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.