Comprehensive Guide to Java synchronized Locks: Usage, Scenarios, and Pitfalls
This article explains the various usages of Java's synchronized keyword, including class locks, object locks, static and instance methods, code blocks, and important considerations such as non‑interruptibility, reentrancy, lack of timeout, and best‑practice performance tips, illustrated with multiple demo programs.
1 Preface
This article uses simple demo programs to illustrate the different usages of the synchronized lock and related precautions, providing a reference for personal memory.
2 synchronized Class Lock
The synchronized lock is a JVM‑built‑in lock, different from ReentrantLock. The keyword can be applied to methods or code blocks, to static or non‑static members, and the scope varies accordingly.
2.1 synchronized on two static methods of the same class are mutually exclusive
public class SynchronizeAndClassLock {
public static void main(String[] args) throws Exception {
new Thread(() -> {
// new a ClassLock object
new ClassLock().test1();
}).start();
new Thread(() -> {
// new another ClassLock object
new ClassLock().test2();
}).start();
}
}
class ClassLock {
public synchronized static void test1() {
System.out.println(new Date() + " " + Thread.currentThread().getName() + " begin...");
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {}
System.out.println(new Date() + " " + Thread.currentThread().getName() + " end...");
}
// Note: public static void test2() will not be mutually exclusive because it is not synchronized.
public synchronized static void test2() {
System.out.println(new Date() + " " + Thread.currentThread().getName() + " begin...");
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {}
System.out.println(new Date() + " " + Thread.currentThread().getName() + " end...");
}
}Result: Two threads executing synchronized static methods on different objects still acquire the same class lock, so they are mutually exclusive. If a method is not synchronized, the two threads will not block each other.
2.2 synchronized on a static method and a static block (class lock) are mutually exclusive
public class SynchronizeAndClassLock2 {
public static void main(String[] args) throws Exception {
new Thread(() -> {
new ClassLock2().test1();
}).start();
new Thread(() -> {
new ClassLock2().test2();
}).start();
}
}
class ClassLock2 {
public synchronized static void test1() {
System.out.println(new Date() + " " + Thread.currentThread().getName() + " begin...");
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {}
System.out.println(new Date() + " " + Thread.currentThread().getName() + " end...");
}
public static void test2() {
// synchronized (SynchronizeAndClassLock2.class) will not be mutually exclusive with test1 because it locks a different class.
synchronized (ClassLock2.class) {
System.out.println(new Date() + " " + Thread.currentThread().getName() + " begin...");
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {}
System.out.println(new Date() + " " + Thread.currentThread().getName() + " end...");
}
}
}Result: A static method synchronized on the class and a static block synchronized on the same class lock each other; changing the lock to another class removes the mutual exclusion.
2.3 synchronized on the same static object are mutually exclusive
public class SynchronizeAndClassLock10 {
public static void main(String[] args) throws Exception {
new Thread(() -> {
new RunObject1().test1();
}).start();
new Thread(() -> {
new RunObject2().test2();
}).start();
}
}
class RunObject1 {
public static void test1() {
synchronized (StaticLock2.staticLock) {
System.out.println(new Date() + " " + Thread.currentThread().getName() + " begin...");
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {}
System.out.println(new Date() + " " + Thread.currentThread().getName() + " end...");
}
}
}
class RunObject2 {
public static void test2() {
synchronized (StaticLock2.staticLock) {
System.out.println(new Date() + " " + Thread.currentThread().getName() + " begin...");
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) {}
System.out.println(new Date() + " " + Thread.currentThread().getName() + " end...");
}
}
}
class StaticLock2 {
public static Object staticLock = new Object();
}Result: When both methods synchronize on the same static object, they block each other; using different static objects removes the mutual exclusion.
3 synchronized Object Lock
The object lock works at the instance level; different instances do not block each other.
3.1 synchronized on two non‑static methods of the same object are mutually exclusive
public class SynchronizeAndObjectLock2 {
public static void main(String[] args) throws Exception {
SynchronizeAndObjectLock2 lock = new SynchronizeAndObjectLock2();
new Thread(() -> lock.test1()).start();
new Thread(() -> lock.test2()).start();
}
public synchronized void test1() { /* ... */ }
public synchronized void test2() { /* ... */ }
}Result: Two threads invoking synchronized methods on the same object acquire the same object lock and therefore execute sequentially.
3.2 synchronized on a method and on synchronized(this) are mutually exclusive
public class SynchronizeAndObjectLock3 {
public static void main(String[] args) throws Exception {
SynchronizeAndObjectLock3 lock = new SynchronizeAndObjectLock3();
new Thread(() -> lock.test1()).start();
new Thread(() -> lock.test2()).start();
}
public void test1() {
synchronized (this) {
// critical section
}
}
public synchronized void test2() { /* ... */ }
}Result: The synchronized method and the synchronized block on this share the same object lock, so they block each other.
3.3 synchronized on different objects does not block
public class SynchronizeAndObjectLock {
public static void main(String[] args) throws Exception {
new Thread(() -> new SynchronizeAndObjectLock().test1()).start();
new Thread(() -> new SynchronizeAndObjectLock().test2()).start();
}
public synchronized void test1() { /* ... */ }
public synchronized void test2() { /* ... */ }
}Result: Because each thread creates a separate instance, the two synchronized methods lock different objects and therefore run concurrently.
3.4 synchronized code block on the same object is mutually exclusive
public class SynchronizeAndObjectLock5 {
private Object objectLock = new Object();
public static void main(String[] args) throws Exception {
SynchronizeAndObjectLock5 lock = new SynchronizeAndObjectLock5();
new Thread(() -> lock.test1()).start();
new Thread(() -> lock.test2()).start();
}
public void test1() {
synchronized (objectLock) { /* critical section */ }
}
public void test2() {
synchronized (objectLock) { /* critical section */ }
}
}Result: Both methods synchronize on the same explicit lock object, so they block each other; using different lock objects would remove the blocking.
4 synchronized on class and on object are independent
public class ClassAndObjectLock {
public static void main(String[] args) throws Exception {
new Thread(() -> ClassAndObjectLock.test1()).start();
new Thread(() -> new ClassAndObjectLock().test2()).start();
}
public static void test1() {
synchronized (ClassAndObjectLock.class) { /* class lock */ }
}
public void test2() {
synchronized (this) { /* object lock */ }
}
}Result: Class lock and object lock are independent; they do not interfere with each other.
5 synchronized Lock Considerations
5.1 synchronized lock cannot be interrupted
The demo creates a deadlock with two threads holding each other's locks; interrupting one thread from the main thread does not release the lock because synchronized is non‑interruptible.
public class DeadLockCannotInterruptDemo {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) throws Exception {
Thread threadA = new Thread(() -> {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " get lock1");
try { Thread.sleep(10); synchronized (lock2) { System.out.println(Thread.currentThread().getName() + " get lock2"); } }
catch (InterruptedException e) { e.printStackTrace(); }
}
});
Thread threadB = new Thread(() -> {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " get lock2");
try { Thread.sleep(10); synchronized (lock1) { System.out.println(Thread.currentThread().getName() + " get lock1"); } }
catch (InterruptedException e) { e.printStackTrace(); }
}
});
threadA.start();
threadB.start();
TimeUnit.SECONDS.sleep(3);
System.out.println("main thread begin to interrupt " + threadA.getName());
threadA.interrupt();
}
}5.2 synchronized is re‑entrant
5.2.1 Different methods can call each other
public class SynchronizeAndReentrant {
public static void main(String[] args) throws Exception {
SynchronizeAndReentrant obj = new SynchronizeAndReentrant();
obj.test1();
}
public synchronized void test1() {
System.out.println("test1 method is called...");
test2();
}
public synchronized void test2() {
System.out.println("test2 method is called...");
}
}5.2.2 Same method can call itself recursively
public class SynchronizeAndReentrant2 {
int i = 1;
public static void main(String[] args) throws Exception {
SynchronizeAndReentrant2 obj = new SynchronizeAndReentrant2();
obj.test1();
}
public synchronized void test1() {
System.out.println("test1 method is called " + i++ + "st time...");
while (i < 5) { test1(); }
}
}5.3 synchronized does not support timeout
Unlike ReentrantLock.tryLock, the built‑in synchronized keyword cannot specify a waiting timeout, which may increase the risk of deadlock.
5.4 wait/notify must be used inside a synchronized block
public class NotifyNeedSynchronized {
public static Object lock = new Object();
public static void main(String[] args) throws Exception {
// lock.notify(); // would throw IllegalMonitorStateException
lock.wait(); // also throws IllegalMonitorStateException unless inside synchronized
}
}Calling notify or wait on an object without holding its monitor results in IllegalMonitorStateException.
5.5 Keep synchronized scope as small as possible for performance
Prefer synchronizing only the critical section rather than the whole method. Example from Netty source code shows synchronized blocks around a shared map instead of the whole method.
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
if (childOption == null) { throw new NullPointerException("childOption"); }
if (value == null) {
synchronized (childOptions) { childOptions.remove(childOption); }
} else {
synchronized (childOptions) { childOptions.put(childOption, value); }
}
return this;
}6 Summary
The article reviews the various usages and precautions of the synchronized keyword through concise demos, and will later explore its underlying implementation.
Follow the "Source Code Notes" public account on WeChat. GitHub repository: https://github.com/yuanmabiji/Java-SourceCode-Blogs
Recommended Reading:
I wrote a singleton with DCL, but the interviewer was not satisfied!
What is a database "buffer pool"? (Comprehensive guide)
Six questions why Kafka is so powerful!
A single space caused a "disaster"
"Pitfall" ranking: Top 10 most counter‑intuitive Java features
Explosive! MySQL in 82 pictures
Interview tip: Docker fundamentals explained
Shunfeng Express: Please accept the MySQL soul ten‑pack
Welcome to follow the WeChat public account "Internet Full‑Stack Architecture" for more valuable information.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
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.
