Search⌘ K
AI Features

Threading Fundamentals and Safety

Explore the fundamentals of Java threading, understanding key differences between threads and processes, common multi-threading pitfalls like deadlocks and race conditions, and thread lifecycle behaviors. This lesson prepares you to write safe and efficient concurrent code by highlighting typical synchronization challenges developers face.

We'll cover the following...

In modern applications, performing multiple tasks concurrently is standard practice for optimizing performance. We achieve this concurrency in Java by using threads.

Let’s look at the fundamental differences between processes and threads, along with the common issues we encounter when writing multithreaded code.

AI Powered
Saved
10 attempts left
Reset
Question

What are some of the differences between a process and a thread?

You have 10 attempts to answer.
AI Powered
Saved
10 attempts left
Reset
Question

What are some of the problems with using threads?

You have 10 attempts to answer.
AI Powered
Saved
10 attempts left
Reset
Question

What is a deadlock?

You have 10 attempts to answer.

We can see a classic deadlock scenario in the following example, where two threads attempt to acquire two locks in reverse order.

Java
import java.util.concurrent.CountDownLatch;
class Demonstration {
public static void main(String[] args) {
Deadlock deadlock = new Deadlock();
try {
deadlock.runTest();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
class Deadlock {
private int counter = 0;
private final Object lock1 = new Object();
private final Object lock2 = new Object();
CountDownLatch latch = new CountDownLatch(2);
Runnable incrementer = () -> {
try {
for (int i = 0; i < 100; i++) {
incrementCounter();
System.out.println("Incrementing " + i);
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
};
Runnable decrementer = () -> {
for (int i = 0; i < 100; i++) {
decrementCounter();
System.out.println("Decrementing " + i);
}
};
public void runTest() throws InterruptedException {
Thread thread1 = new Thread(incrementer);
Thread thread2 = new Thread(decrementer);
thread1.start();
Thread.sleep(100);
thread2.start();
thread1.join();
thread2.join();
System.out.println("Done : " + counter);
}
void incrementCounter() throws InterruptedException {
synchronized (lock1) {
latch.countDown();
System.out.println("Acquired lock1");
latch.await();
synchronized (lock2) {
counter++;
}
}
}
void decrementCounter() {
synchronized (lock2) {
System.out.println("Acquired lock2");
latch.countDown();
synchronized (lock1) {
counter--;
}
}
}
}

Note: This program is intentionally designed to demonstrate a deadlock. After both threads acquire different locks and wait for each other's lock, neither thread can continue. As ...