Eight Java synchronized Method Scenarios and Their Thread‑Safety Analysis
This article presents eight common multithreading scenarios involving Java synchronized methods, explains whether each case is thread‑safe, provides detailed code demonstrations, shows execution results, and summarizes the underlying lock mechanisms that determine safety.
This article introduces eight typical usage scenarios of Java synchronized methods, analyzes whether each scenario is thread‑safe, and explains the reasons behind the results. These scenarios are frequently encountered in multithreaded programming and are common interview questions.
Scenario 1: Two threads access the synchronized method of the same object
Analysis: Both threads compete for the same object lock, so the execution is serialized and thread‑safe.
Conclusion: "Two threads accessing the same object's synchronized method is thread‑safe."
public class Condition2 implements Runnable {
static Condition2 instance1 = new Condition2();
static Condition2 instance2 = new Condition2();
@Override
public void run() { method(); }
private synchronized void method() {
System.out.println("Thread: " + Thread.currentThread().getName() + ", start");
try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread: " + Thread.currentThread().getName() + ", end");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start(); t2.start();
while (t1.isAlive() || t2.isAlive()) {}
System.out.println("Test finished");
}
}Running result shows parallel execution, confirming the lock is ineffective because each thread holds a different object lock.
Scenario 2: Two threads access synchronized methods of two different objects
Analysis: Object locks are independent, so the threads do not contend for the same lock and the scenario is not thread‑safe.
Conclusion: "Two threads accessing synchronized methods of two different objects is not thread‑safe."
Thread name: Thread-0, start
Thread name: Thread-1, start
Thread: Thread-0, end
Thread: Thread-1, end
Test finishedScenario 3: Two threads access (one or both) static synchronized methods
Analysis: Static synchronized methods use the class lock ( *.class ), so all threads compete for the same lock, making the scenario thread‑safe.
Conclusion: "Two threads accessing static synchronized methods (whether on the same or different objects) is thread‑safe."
Scenario 4: One thread accesses a synchronized method while another accesses a non‑synchronized method
Analysis: The non‑synchronized method does not acquire the lock, so the two threads run concurrently; the scenario is not thread‑safe.
Conclusion: "One thread calling a synchronized method and another calling a non‑synchronized method is not thread‑safe."
public class Condition4 implements Runnable {
static Condition4 instance = new Condition4();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) method0();
if (Thread.currentThread().getName().equals("Thread-1")) method1();
}
private synchronized void method0() { /* synchronized work */ }
private void method1() { /* ordinary work */ }
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start(); t2.start();
while (t1.isAlive() || t2.isAlive()) {}
System.out.println("Test finished");
}
}Scenario 5: A synchronized method calls a non‑synchronized method
Analysis: If the non‑synchronized method is invoked only from the synchronized method, it remains thread‑safe; however, if other threads call the non‑synchronized method directly, the overall operation becomes unsafe.
Conclusion: "Two threads accessing a synchronized method that internally calls a non‑synchronized method are thread‑safe only when no other thread invokes the non‑synchronized method directly."
Scenario 6: Two threads access different synchronized methods of the same object
Analysis: Both methods lock on the same object (this), so the execution is serialized and thread‑safe.
Conclusion: "Two threads accessing different synchronized methods of the same object is thread‑safe."
public class Condition5 implements Runnable {
static Condition5 instance = new Condition5();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) method0();
if (Thread.currentThread().getName().equals("Thread-1")) method1();
}
private synchronized void method0() { /* ... */ }
private synchronized void method1() { /* ... */ }
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start(); t2.start();
while (t1.isAlive() || t2.isAlive()) {}
System.out.println("Test finished");
}
}Scenario 7: One thread accesses a static synchronized method while another accesses a non‑static synchronized method
Analysis: The static method locks on the class object, the non‑static method locks on the instance (this); the locks are different, so the threads run concurrently and the scenario is not thread‑safe.
Conclusion: "Static synchronized + non‑static synchronized methods accessed simultaneously are not thread‑safe."
public class Condition6 implements Runnable {
static Condition6 instance = new Condition6();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) method0();
if (Thread.currentThread().getName().equals("Thread-1")) method1();
}
private static synchronized void method0() { /* class‑level work */ }
private synchronized void method1() { /* instance‑level work */ }
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start(); t2.start();
while (t1.isAlive() || t2.isAlive()) {}
System.out.println("Test finished");
}
}Scenario 8: A synchronized method throws an exception
Analysis: The JVM releases the lock when the synchronized method exits normally or throws an exception, allowing other threads to acquire the lock without deadlock.
Conclusion: "Only normal completion or exception exit releases the lock; an exception does not cause deadlock."
public class Condition7 implements Runnable {
private static Condition7 instance = new Condition7();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) method0();
if (Thread.currentThread().getName().equals("Thread-1")) method1();
}
private synchronized void method0() {
System.out.println("Thread-0 start");
try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread-0 throws exception");
throw new RuntimeException();
}
private synchronized void method1() {
System.out.println("Thread-1 start");
try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Thread-1 end");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start(); t2.start();
while (t1.isAlive() || t2.isAlive()) {}
System.out.println("Test finished");
}
}Summary
The article systematically demonstrates how the synchronized keyword determines lock objects (instance lock vs. class lock) and how different lock scopes affect thread safety. By analyzing these eight scenarios, developers can reason about concurrency issues and avoid common pitfalls in multithreaded Java programs.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.