Deadlocks, Starvation, and Race Conditions

Deadlocks, Starvation, and Race Conditions

In the Java programming language, when multiple threads execute or run simultaneously, these thread tasks often need to share thread task resources, such as program-declared variables, objects, files, or database connections. If access to resources within the current thread program cannot be managed in the proper order, this can lead to thread problems or issues such as deadlock, starvation, and race conditions.

Deadlocks, Starvation, and Race Conditions

Deadlock condition in Java Threads.

A deadlock condition develops in a Java thread program when two or more thread tasks in a thread task program are constantly waiting for each other to release system resources or locks.

In simple terms, a Java thread deadlock occurs when thread task 1 is waiting for thread task 2, while thread task 2 is waiting for thread task 1.

As a result, no thread task can allow execution of another thread task.

Example of a deadlock in Java threads.

Let’s assume that the Java user has defined two thread task locks.

ThreadObject lock1 = new ThreadObject();

ThreadObject lock2 = new ThreadObject();

In this example, thread task 1 locks and secures lock1, then waits for lock2.

Thread task 2 locks and secures lock2, then waits for lock1.

class SampleDeadlock {

static ThreadObject lock1 = new ThreadObject();

static ThreadObject lock2 = new ThreadObject();

public static void main(String[] args) {

Thread th1 = new Thread(() -> {

synchronized (lock1) {

System.out.println(“Thread 1 Result – it locked lock1”);

synchronized (lock2) {

System.out.println(“Thread 1 Result – it locked lock2”);

}

}

});

Thread th2 = new Thread(() -> {

synchronized (lock2) {

System.out.println(“Thread 2 Result – it locked lock2”);

synchronized (lock1) {

System.out.println(“Thread 2 Result – it locked lock1”);

}

}

});

th1.start();

th2.start();

}

}

What happens in thread deadlock?

thread task 1 thread task 2

| |

v v

Lock thread lock1, lock thread lock2

| |

v v

In this case, the user wants thread lock2 or thread lock1.

| |

+——– WAIT ———+

In this case, thread task 1 cannot acquire lock2 because it is held by thread task 2.

Similarly, thread task 2 cannot acquire lock1 because it is held by thread task 1.

In this thread deadlock condition, both threads continue to wait for the lock.

How to avoid a deadlock condition in Java threads?

A thread task is a basic method in a Java program, allowing Java users to always acquire thread task locks in the same order.

For example, always.

Thread task lock1 → Thread task lock2

and never.

Thread task lock2 → Thread task lock1

Starvation condition in Java Threads.

A starvation condition in a Java thread program occurs when a user-defined thread task is denied continuous access to a system program resource or CPU microprocessor time. This condition allows the system to alternate between executing other thread task activities.

In Java, a thread starvation task occurs when a thread in a system program is ready to start a task, yet the system continues to ignore or avoid that task.

For example, suppose we have multiple threads trying to access a synchronized system resource. If, in such a situation, one thread task repeatedly acquires the lock, while another thread task simultaneously waits for the lock, the waiting thread task may experience starvation.

Example of a conceptual thread task in Java.

Thread Task X → Thread Task X gets resources from a system program.

Thread Task Y → Here, thread task Y is on the waiting list.

Thread Task X → Again, thread task X gets resources from a system program.

Thread Task Y → Here, thread task Y is on the waiting list.

Thread Task X → Again, thread task X gets resources from a system program.

Thread Task Y → Still, thread task Y is on the waiting list.

Here, in the above thread task, Y can still be in the waiting process for a very long time.

Main causes of Java thread starvation.

  • Poor scheduling of thread tasks
  • False allocation of thread task resources
  • When a single thread holds a lock value for too long
  • When a high-priority thread task in the system repeatedly receives CPU time
  • User-defined false synchronization/access to a resource

How can Java thread starvation be reduced?

  • Avoid programs holding locks for unnecessarily long periods of time.
  • Always keep your synchronized code sections short.
  • Use appropriate concurrency utilities when necessary.
  • Avoid unnecessarily relying on thread task priorities in the system.
  • Properly define and design program system resource access.

Race Conditions in Java Threads.

A race condition in a Java thread program develops when multiple thread tasks on a system CPU simultaneously access and modify shared data, and the final result output depends entirely on the real-time or order of execution of those thread tasks.

A Java thread race condition task, simply put, is a situation where thread tasks “race” or compete with each other to access shared system or program data.

Example of a Race Condition.

Here, considers a condition.

class Tester {

int count_value = 0;

void increase() {

count_value++;

}

}

Suppose we execute two thread tasks simultaneously in this program.

Tester.increase();

Here, you can expect the following in this thread task.

Initial count_value = 0

Thread Task 1 → increase

Thread Task 2 → increase

Now final count_value = 2

But count_value++ is not an operation that cannot be defined separately. According to the definition concept, it includes:

Read count value

↓

Add 1 value

↓

Write new count value

Thread tasks can be executed like this:

Thread Task 1 Thread Task 2

Read count value = 0

Read count value = 0

Add 1 value

Add 1 value

Write 1 value

Write 1 value

Final count value.

1

Instead.

2

This is a simple race condition method.

Preventing a race condition in a thread task.

Java users can use a synchronized keyword method in this condition.

class Tester {

int count_value = 0;

synchronized void increase() {

count_value++;

}

}

Now, after this condition, Java users can only have one thread run the increase() function on a single Tester object at a time.

Another option is to define an atomic class for proper program operation, such as using AtomicInteger.

import java.util.concurrent.atomic.AtomicInteger;

class Tester {

AtomicInteger count_value = new AtomicInteger(0);

void increase() {

count_value.increaseAndGet();

}

}

Main Difference Between Deadlock, Starvation, and Race Condition

SituationEach meaningWhat is causeOutput
DeadlockIn deadlock user define threads task wait for each other foreverIt causes circular lock dependency on your thread taskIn this stage your threads task stops making progress
StarvationIn starvation mode a thread keeps waiting for a resource/cpu to allocateIt causes of unfair scheduling/resource allocation of cpu or program created taskIn this stage one thread gets little or no progress during previous complete
Race conditionIn race condition output result depends on timing of threads task execution processIt causes unsynchronized shared data access of resourcesIn this stage incorrect or unpredictable false output result

An easy way to remember deadlock, starvation, and race conditions in Java programs.

Deadlock: A Java thread condition.

In a Java thread deadlock task, “You wait for me, I’ll wait for you.”

X → waits for Y

Y → waits for X

Starvation: A Java thread condition.

In a Java thread starvation task, “Everyone gets system program or CPU resources except me.”

X → X thread task gets system program resources

X → X thread task gets system program resources

X → X thread task gets system program resources

Y → In this condition, Y thread task keeps waiting for Y.

Race condition: A Java thread condition.

In a Java race condition, “We both change the same program system data at the same time.”

Thread Task 1 ──┐

├──> shared system program data

Thread Task 2 ──┘

Conclusion of Deadlocks, Starvation, and Race Conditions.

A deadlock in Java thread tasks is a system-defined condition where two or more thread tasks wait indefinitely for a resource or lock held by each other. This prevents the thread tasks from progressing.

Starvation in Java thread tasks occurs when a thread task is denied consistent access to CPU time or a critical program system resource because other threads repeatedly gain access to it.

A race condition in Java thread tasks occurs when multiple user-defined threads simultaneously access and modify shared system program data, and the final result depends entirely on the timing or order of thread execution. Synchronization mechanisms in Java, such as synchronized, locks, and the atomic class, can be used to prevent race conditions.

One-line summary of Deadlocks, Starvation, and Race Conditions.

  • Deadlock stage → In this stage, the Java program creates threads that are always waiting.
  • Starvation stage → In this stage, a thread waits too long.
  • Race condition → In this stage, simultaneous thread task access may cause false results to be displayed.

Leave a Reply