DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Mastering Thread-Local Variables in Java: Explanation and Issues
  • Unlocking Performance: Exploring Java 21 Virtual Threads [Video]
  • Visualizing Thread-Safe Singletons in Java
  • Double-Checked Locking Design Pattern in Java

Trending

  • Blue Skies Ahead: An AI Case Study on LLM Use for a Graph Theory Related Application
  • Medallion Architecture: Why You Need It and How To Implement It With ClickHouse
  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  • DGS GraphQL and Spring Boot
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Intricacies of Multi-Threading in Java

Intricacies of Multi-Threading in Java

You can never have enough threads. Well, you can, but be sure you know how they work first. This is a comprehensive look at multi-threading throughout the ages.

By 
Sumith Puri user avatar
Sumith Puri
·
Apr. 15, 17 · Tutorial
Likes (43)
Comment
Save
Tweet
Share
35.2K Views

Join the DZone community and get the full member experience.

Join For Free

Multi-threading represents a very intriguing topic, even after years of research and development for high quality, robust, and efficient software. With equal emphasis on hardware improvements and the software that runs on it – we have newer paradigms for parallelism. The most important yet basic concepts are the ones which I present here. I then explain the intricacies of multi-threading in the Java programming language. Some of these are newer features and supported only from the Java Platform Standard Edition 5.0. Let us start with a quick overview and understanding of the core concepts.

Thread Concepts in Operating Systems           

Thread: A thread is a lightweight process, but it differs from a process in multiple ways. The primary features of a thread are that it creates a sense of execution of processes concurrently; it effectively distributes work and executes a single task, it can be used to schedule and asynchronously execute tasks, it has a more effective context switching mechanism than processes, and it shares the memory space along with other threads yet can have its own storage

Critical Section: The critical section of the code is identified as the focus area that will be accessed by multiple threads concurrently and can modify the state. Since there is concurrent modification while sharing a resource, there needs to be controlled access.

Semaphores: Semaphore is a resource count that can be used to control access to a shared resource, especially in a multi-threaded environment.

Mutex: Mutex is a synchronization construct that allows only one thread a time to use a resource and has ownership associated with it.

Monitor: A synchronization construct that allows both mutual exclusion and the ability to wait for a condition to be true.

Deadlocks: While accessing the critical section of the code, there might be situations where multiple shared resources are accessed by multiple threads. In such scenarios, when a single thread requires access to a shared resource, it may first need to relieve the currently shared resource that is holding. It may also be true of another concurrently executing thread with respect to the same resources. When such mutual exclusion cannot be achieved, it leads to a Deadlock.

Deadlock Prevention: The techniques involved in preventing deadlock conditions from arising constitute Deadlock Prevention.

Deadlock Handling: Detection of deadlocks and its removal constitute Deadlock Handling.

Reentrancy: When a method or subroutine can be reentered without completing its previous invocation.

Image title

Thread Interfaces or Classes or in Java

Thread: The important methods of Thread Class are explained and usage shown in the code samples. The most confusing and common errors even for experienced Java programmers in multi-threading is in understanding locks. I have provided the status of locks on shared objects during each of these methods.

Runnable: An alternate way to create a Thread is by implementing this interface. One can create a thread in Java by either of implementing a Runnable interface or extending the Thread class. The programmer will have to override the run() method from this interface to implement logic.

  • start(), Lock Status: Can Acquire Locks; This is the method called to schedule a thread to run. Once scheduled and a CPU cycle is available the thread actually runs.

  • run(), Lock Status: Can Acquire Locks; It is implicitly called by the Thread runtime to start executing a thread.

  • yield(), Lock Status: Locks Held; This method yields or transfers control to another thread of equal priority. It does not provide any guarantee regarding which thread it will transfer control to or whether it will at all. The code snippet below also shows how to create a Thread by extending the Thread class. 

  • sleep(), Lock Status: Locks Held; This method causes the current thread in execution to pause execution for a given time period. The time period is specified in milliseconds. It throws an interrupted exception, which needs to be handled by the programmer.

  • join(), Lock Status: Locks Held; This leads to an execution mode where all threads join at the end of the current thread. In other words, the current thread is taken to completion before switching to another thread. It throws an interrupted exception, which the programmer has to handle.

  • suspend(), Lock Status: Locks Held, Deprecated.

  • resume(), Lock Status: Locks Held, Deprecated; These thread methods are deprecated as it could lead to deadlocks and frozen processes. This is especially when the thread that has to resume a suspended thread requires access to the shared resource or lock held by the suspended thread before invoking resume().

  • stop(), Lock Status: Locks Released, Deprecated; This thread method is deprecated as it produced inconsistent states due to damaged objects. I am not providing the code sample as it is pretty straight forward usage – but I will not recommend the use of these methods, even if using a very old compiler version.

Object in Java

The Object class in Java inherently contains the methods that can control access to this object, especially in shared or multi-threaded applications.

  • wait(), Lock Status: Current Object Lock Released, OtherLocks Held; The wait() method causes the current thread to pause execution and move to a wait state. It also releases the lock that it holds on the current object but retains all other locks on other objects.

  • notify(), Lock Status: Acquisition of Lock by an Arbitrary Waiting Thread; The notify() method notifies an arbitrary thread that is waiting to obtain a lock on the currently shared object.

  • notifyAll(), Lock Status: Acquisition of Lock by an Arbitrary Waiting Thread; The notifyAll() counterpart of this method notifies all threads that are waiting to acquire a lock on the shared object.

Note on locks: You may use this thought process whenever you think of Locks or Monitors in Java – Any concurrent modification by Threads should not lead to a damaged object. The only exception being the wait() and notify() mechanisms which may cause changes to a shared resource or object before swapping or switching control. A damaged object is one in which undesirable or corruptible changes have occurred to the state.

synchronized: In Java, the synchronized keyword is used to control access to the critical section of the code. Alternatively, it is the implementation of Thread Monitors in Java. The synchronized keyword can be applied to both static methods or to instance level methods or blocks. When a thread enters a synchronized block or method, it obtains the lock on that class or object. In the case of static synchronized methods, a single lock is held at the class level and is different from an instance level lock that is held per instance of the class. The synchronized keyword provides the necessary mutual exclusion on a shared resource. wait(), notify(), and notifyAll() can only be called in synchronized blocks or methods. Mutexes are not inherently supported in Java. 

Case Study: Design a multi-threaded system that has a shared resource that can take only two values; 0 or 1. It should have two methods, one each for incrementing and decrementing that are called by two threads concurrently. One of the threads can only constantly increment and another can only constantly decrement. Their operations should be mutually exclusive.

Solution: It is a simplified version of the Producer-Consumer problem.

package org.csi_india.programming.workbench.multithreading;
public class CSIDecrementer implements Runnable {
    CSISharedObject csiSharedObject;
    CSIDecrementer(CSISharedObject csiSharedObject) {
        this.;
    }
    public void run() {
        while(true) csiSharedObject.decrementerAccess();
    }
}


package org.csi_india.programming.workbench.multithreading;
public class CSIIncrementer implements Runnable {
    CSISharedObject csiSharedObject;
    CSIIncrementer(CSISharedObject csiSharedObject) {
        this.;
    }
    public void run() {
        while(true) csiSharedObject.incrementerAccess();
    }                                                          
}


package org.csi_india.programming.workbench.multithreading;
public class CSIWorkbench extends Thread {
    public static void main(String[] args) {
        CSISharedObject csiShared=new CSISharedObject();
        Thread csiThread01=new Thread(new CSIIncrementer(csiShared));
        csiThread01.start();
        Thread csiThread02=new Thread(new CSIDecrementer(csiShared));
        csiThread02.start();
    }
}


package org.csi_india.programming.workbench.multithreading;
public class CSISharedObject {
    // access from within this class only
    private int;
    public synchronized void decrementerAccess() {
        try {
            if (x = ) {
                x--;
                notify();
            } else {
                wait();
            }
        } catch (InterruptedException e) {
            System.out.println("thread interrupted");
        }
    }
    public synchronized void incrementerAccess() {
        try {
            if (x = ) {
                x++;
                notify();
            } else {
                wait();
            }
        } catch (InterruptedException e) {
            System.out.println("thread interrupted");
        }
    }
}


You may refer to the following section, as these tools were introduced only since JDK 5 and 6. They provide a more granular or controlled access for asynchronous task execution in Java.

  • Callable: Another class similar to Runnable, whose instances are potentially executed by another Thread.

  • Executors: A helper interface for creation of thread pool.

  • ExecutorService: An asynchronous task executor that can be used to submit Runnable or Callable tasks for execution and then to track their status through a Future object.

  • Future: An object return from task submission to an asynchronous task executor using which a task state can be monitored.

  • AtomicInteger: A type of Integer object in Java that performs concurrent lock-free updates using hardware instructions.

Case Study: Design an asynchronous task executor modeled as a thread pool executor.

Solution: It is a simplified version of the Producer-Consumer problem.

Most of the below classes were introduced since JDK 7. They provide an alternate and simpler way of programming multi-threaded applications in Java.

Condition: Condition factors out the Object monitor methods (wait, notify and notifyAll() into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods. Condition is a Java interface.

signal(): Wakes up one waiting Thread.

signalAll(): Wakes up all waiting Threads.

await(): Causes the current Thread to wait until it is signaled or interrupted. Lock: Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. Lock is an interface.

ReentrantLock: A reentrant mutual exclusion lock with the same basic behavior as implicit monitor lock. It is a concrete implementation of Lock.

Happy Multi-Threading in Java!

Java (programming language) Threading

Published at DZone with permission of Sumith Puri. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Mastering Thread-Local Variables in Java: Explanation and Issues
  • Unlocking Performance: Exploring Java 21 Virtual Threads [Video]
  • Visualizing Thread-Safe Singletons in Java
  • Double-Checked Locking Design Pattern in Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!