Understanding Java's synchronized(this) Object Lock and Thread Interaction
This article explains how Java's synchronized(this) keyword creates an object lock that allows only one thread to execute a synchronized block at a time, demonstrates various scenarios with code examples, and shows how non‑synchronized sections remain concurrent while all synchronized sections on the same object are blocked.
Java's synchronized keyword, when used to guard a method or a code block (e.g., synchronized(this) ), ensures that at most one thread can execute that section of code at any given moment by acquiring the object's monitor lock.
Example 1: Two concurrent threads invoke a synchronized(this) block on the same object. The output shows that thread A completes all iterations before thread B starts.
package ths;
public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}Result:
A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4Example 2: While one thread holds the object lock inside a synchronized block, another thread can still execute non‑synchronized code of the same object, resulting in interleaved output.
package ths;
public class Thread2 {
public void m4t1() {
synchronized(this) {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}
}
public void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread(() -> myt2.m4t1(), "t1");
Thread t2 = new Thread(() -> myt2.m4t2(), "t2");
t1.start();
t2.start();
}
}Result (interleaved):
t1 : 4
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0Example 3: If the second method is also wrapped in a synchronized block on the same object, the second thread must wait until the first releases the lock, producing sequential output.
public void m4t2() {
synchronized(this) {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}
}Result:
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0Example 4: Declaring the method as synchronized yields the same blocking behavior because the method acquires the object's monitor.
public synchronized void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}Result is identical to Example 3.
Example 5: The lock applies to any synchronized block or method that uses the same object. An inner class demonstrates that a synchronized block on an Inner instance blocks other synchronized sections on that same instance, while non‑synchronized code runs concurrently.
package ths;
public class Thread3 {
class Inner {
private void m4t1() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}
private void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try { Thread.sleep(500); } catch (InterruptedException ie) {}
}
}
}
private void m4t1(Inner inner) {
synchronized(inner) { // object lock
inner.m4t1();
}
}
private void m4t2(Inner inner) {
inner.m4t2();
}
public static void main(String[] args) {
final Thread3 myt3 = new Thread3();
final Inner inner = myt3.new Inner();
Thread t1 = new Thread(() -> myt3.m4t1(inner), "t1");
Thread t2 = new Thread(() -> myt3.m4t2(inner), "t2");
t1.start();
t2.start();
}
}Result shows that t1 acquires the lock on inner , so t2 's call to m4t2 (which is not synchronized) proceeds without waiting, demonstrating that only synchronized sections are blocked.
In summary, acquiring an object lock via synchronized(this) or a synchronized method blocks all other synchronized blocks or methods on the same object, while code that is not synchronized can still execute concurrently.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
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.