Skip to Content
Course content

82: The synchronized Keyword

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're at a busy coffee shop that has exactly one single-occupancy restroom. To keep things orderly, there is a lock on the door. When a customer goes in, they lock the door behind them. If another customer tries to enter, they find the door locked and simply wait in line. Once the first person leaves and unlocks the door, the next person in line can step in and lock it for themselves.

In Java, the synchronized keyword is that lock. Without it, you have what we call a "race condition," which is basically the software equivalent of three people trying to use the same restroom at the exact same time. It's messy, it's chaotic, and in a production environment, it leads to data corruption that is a nightmare to debug because it only happens occasionally.

The Chaos of Concurrent Access

Let's look at a real scenario. Suppose we're building a simple bank account system. We have a balance, and we have a method to withdraw money. If only one thread is running, everything is fine. But if two threads (representing two different ATMs) try to withdraw money from the same account at the exact same millisecond, we run into trouble.

public class BankAccount {
    private int balance = 100;

    public void withdraw(int amount) {
        if (balance >= amount) {
            System.out.println(Thread.currentThread().getName() + " is about to withdraw...");
            balance -= amount;
            System.out.println(Thread.currentThread().getName() + " finished. Balance: " + balance);
        } else {
            System.out.println("Insufficient funds for " + Thread.currentThread().getName());
        }
    }
}

Here is the danger: Thread A checks the balance and sees $100. Before it can actually subtract the money, the CPU switches to Thread B. Thread B also sees $100. Both threads think they have enough money, both subtract $80, and suddenly your balance is -$60. The bank is unhappy, and your code is broken.

Putting a Lock on the Door

To fix this, we use the synchronized keyword. When you mark a method as synchronized, Java ensures that only one thread can execute that method on a specific instance of the object at a time. It's like the restroom lock—the first thread to enter "grabs the lock," and all other threads must wait until that method finishes.

public synchronized void withdraw(int amount) {
    if (balance >= amount) {
        balance -= amount;
        System.out.println(Thread.currentThread().getName() + " withdrew " + amount);
    }
}

Now, if Thread A enters withdraw(), it locks the entire BankAccount object. If Thread B tries to call withdraw() on that same object, it's blocked. It stays in a "Blocked" state until Thread A exits the method and releases the lock. I've spent way too many hours of my career chasing bugs that were caused by forgetting this one keyword.

Precision Locking with Synchronized Blocks

Now, here is a pro tip: synchronizing an entire method is easy, but it can be slow. If your method does a lot of work (like calling a slow web service) and only one small part of it actually touches the shared data, you're locking the door for way longer than you need to. This kills your application's performance.

Instead, you can use a synchronized block. This allows you to lock only the specific lines of code that are "critical." You have to tell Java which object to use as the lock (usually this).

public void withdraw(int amount) {
    // Non-critical code here (e.g., logging, validation)
    System.out.println("Processing request for " + Thread.currentThread().getName());

    synchronized(this) {
        // Only this part is locked
        if (balance >= amount) {
            balance -= amount;
        }
    }
    
    // More non-critical code here
    System.out.println("Transaction complete.");
}

By narrowing the scope, you let other threads do the "non-critical" work in parallel, only making them wait for the actual balance update. It's the difference between locking the entire coffee shop or just locking the restroom door.




📋 Practical Task

Fixing the Double-Booked Concert Seat

You are building a ticket booking system. Currently, the system has a bug where two users can book the same last remaining seat because the bookSeat() method isn't thread-safe. Your task is to implement synchronization to ensure that only one person can successfully book the final seat.

public class TicketSystem {
    private int availableSeats = 1;

    public void bookSeat(String customerName) {
        if (availableSeats > 0) {
            System.out.println(customerName + " is booking the seat...");
            try {
                // Simulating a slight delay in processing
                Thread.sleep(100); 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            availableSeats--;
            System.out.println("Seat booked successfully for: " + customerName);
        } else {
            System.out.println("Sorry " + customerName + ", the seats are sold out!");
        }
    }

    public static void main(String[] args) {
        TicketSystem system = new TicketSystem();

        // Two people trying to book the same last seat simultaneously
        Thread user1 = new Thread(() -> system.bookSeat("Alice"), "User-1");
        Thread user2 = new Thread(() -> system.bookSeat("Bob"), "User-2");

        user1.start();
        user2.start();
    }
}

Goal: Modify the TicketSystem class so that only one user (either Alice or Bob) gets the seat, and the other receives the "sold out" message. You can use either a synchronized method or a synchronized block.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.