Master Java Concurrency: Synchronized, Locks, Wait vs Sleep, and Deadlock Explained

This article explains Java multithreading safety issues, demonstrates synchronized methods, blocks, static methods, mutex locks, deadlock scenarios, and the differences between wait() and sleep() with clear code examples and analysis of their effects on thread execution.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Master Java Concurrency: Synchronized, Locks, Wait vs Sleep, and Deadlock Explained

1. What is multithreaded concurrency safety issue?

When multiple threads operate on the same data concurrently, the unpredictable execution order can cause data inconsistency and even system crash.

2. Methods synchronized with synchronized

A method declared with synchronized becomes a synchronized method; only one thread can execute its body at a time, preventing concurrency safety problems.

Example: Bean grabbing (prevent negative count)

public class SyncDemo {
    public static void main(String[] args) {
        Table table = new Table();
        Thread t1 = new Thread() {
            public void run() {
                while (true) {
                    int n = table.getBean();
                    Thread.yield();
                    System.out.println(Thread.currentThread().getName() + ", beans left " + n);
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                while (true) {
                    int n = table.getBean();
                    Thread.yield();
                    System.out.println(Thread.currentThread().getName() + ", beans left " + n);
                }
            }
        };
        t1.start();
        t2.start();
    }
}
class Table {
    private int bean = 10;
    public synchronized int getBean() {
        if (bean == 0) {
            throw new RuntimeException("No beans left");
        }
        Thread.yield(); // simulate thread switch
        return bean--;
    }
}

Result: when bean count reaches 0 the program throws an exception; without locking the count may become negative.

3. Synchronized blocks

Limiting the synchronized scope improves efficiency while guaranteeing thread safety.

Example: Simulated shopping

public class Syncdemo2 {
    public static void main(String[] args) {
        Shop shop = new Shop();
        Thread t1 = new Thread() {
            public void run() { shop.buy(); }
        };
        Thread t2 = new Thread() {
            public void run() { shop.buy(); }
        };
        t1.start();
        t2.start();
    }
}
class Shop {
    public void buy() {
        try {
            String name = Thread.currentThread().getName();
            System.out.println(name + " selects clothes");
            Thread.sleep(2000);
            synchronized (this) {
                System.out.println(name + " tries clothes");
                Thread.sleep(2000);
            }
            System.out.println(name + " checks out");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The "try clothes" step is executed sequentially because of the synchronized block.

4. Synchronized static methods

Static synchronized methods lock on the Class object, ensuring that only one thread can execute any static synchronized method of that class at a time.

public class Syncdemo3 {
    public static void main(String[] args) {
        Thread t1 = new Thread() { public void run() { Foo.dosome(); } };
        Thread t2 = new Thread() { public void run() { Foo.dosome(); } };
        t1.start();
        t2.start();
    }
}
class Foo {
    public synchronized static void dosome() {
        try {
            String name = Thread.currentThread().getName();
            System.out.println(name + " is running dosome");
            Thread.sleep(3000);
            System.out.println(name + " finished");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. Mutual exclusion lock

When multiple synchronized code sections lock on the same object, they form a mutex; only one thread can execute any of them at a time.

public class Syncdemo4 {
    public static void main(String[] args) {
        Eoo eoo = new Eoo();
        Thread t1 = new Thread() { public void run() { eoo.test01(); } };
        Thread t2 = new Thread() { public void run() { eoo.test02(); } };
        t1.start();
        t2.start();
    }
}
class Eoo {
    public synchronized void test01() {
        // ...
    }
    public synchronized void test02() {
        // ...
    }
}

6. Deadlock

Deadlock occurs when two threads each hold a lock and wait for the other’s lock, causing the program to stall.

public class Syncdemo5 {
    public static void main(String[] args) {
        Poo p = new Poo();
        Thread t1 = new Thread() { public void run() { p.method1(); } };
        Thread t2 = new Thread() { public void run() { p.method2(); } };
        t1.start();
        t2.start();
    }
}
class Poo {
    Object A = new Object();
    Object B = new Object();
    public void method1() {
        synchronized (A) {
            // work ...
            method2(); // tries to acquire B
        }
    }
    public void method2() {
        synchronized (B) {
            // work ...
            method1(); // tries to acquire A
        }
    }
}

The program prints partial progress then hangs because each thread waits for the other’s lock.

7. Difference between wait() and sleep()

wait() is defined in Object, sleep() in Thread.

wait() releases the monitor lock; sleep() does not.

wait() must be called inside a synchronized block; sleep() can be called anywhere (but must handle InterruptedException).

wait() can be notified by notify/notifyAll; sleep() cannot be notified.

public class WaitDemo {
    public static void main(String[] args) {
        Thread t1 = new ThreadMy01();
        Thread t2 = new ThreadMy02();
        t1.start();
        t2.start();
    }
}
class ThreadMy01 extends Thread {
    static StringBuilder str = new StringBuilder();
    public void run() {
        synchronized (str) {
            for (int i = 0; i < 5; i++) {
                try {
                    str.wait(300);
                    str.append('a');
                    System.out.println(Thread.currentThread().getName() + str);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
class ThreadMy02 extends Thread {
    public void run() {
        synchronized (ThreadMy01.str) {
            for (int i = 0; i < 2; i++) {
                try {
                    Thread.sleep(2000);
                    System.out.println("888");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            ThreadMy01.str.notify();
        }
    }
}

Output shows that sleep does not release the lock, while wait does.

Thread lifecycle

Thread lifecycle diagram
Thread lifecycle diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencydeadlockSynchronizationmultithreadingsleepwait
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.