无Object Locking实现代码块原子执行的多线程同步方案问询
Hey there! Let's figure out how to make those two print statements run atomically without relying on dummy synchronized objects or wait/notify. The core issue here is preventing other threads from interrupting the pair of println calls—so we need a way to guarantee those two lines execute back-to-back, no interruptions.
Solution 1: Synchronize on System.out itself
Since all your threads are using System.out to print, you can use this object as your lock. It’s a natural, existing object (no dummy required) and ensures only one thread can execute the block of print statements at a time. Here’s how to modify your code:
public class Main { public static void main(String[] args) { int sleepTime = 10, N = 1000; Thread t1 = new Thread(() -> { for (int i = 0; i < N; i++) { synchronized (System.out) { // Lock on System.out directly System.out.println("Thread one rules!!"); System.out.println("Im in 1!!!\n"); } try { Thread.sleep(sleepTime); } catch (InterruptedException e) {} } }); Thread t2 = new Thread(() -> { for (int i = 0; i < N; i++) { synchronized (System.out) { System.out.println("Thread two rules!!"); System.out.println("Im in 2!!!\n"); } try { Thread.sleep(sleepTime); } catch (InterruptedException e) {} } }); t1.start(); t2.start(); t1.join(); t2.join(); } }
This works because System.out is a shared object across all threads, so synchronizing on it ensures mutual exclusion for your print block. No dummy objects needed, and it’s straightforward.
Solution 2: Use ReentrantLock for more control
If you want a more flexible locking mechanism (great for scaling to hundreds of threads), use java.util.concurrent.locks.ReentrantLock. It’s more performant in high-concurrency scenarios and gives you explicit control over locking/unlocking:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Global lock instance shared by all threads private static final Lock printLock = new ReentrantLock(); public static void main(String[] args) { int sleepTime = 10, N = 1000; Thread t1 = new Thread(() -> { for (int i = 0; i < N; i++) { printLock.lock(); // Acquire the lock try { // Critical section: these lines run atomically System.out.println("Thread one rules!!"); System.out.println("Im in 1!!!\n"); } finally { printLock.unlock(); // Always release the lock, even if an exception occurs } try { Thread.sleep(sleepTime); } catch (InterruptedException e) {} } }); Thread t2 = new Thread(() -> { for (int i = 0; i < N; i++) { printLock.lock(); try { System.out.println("Thread two rules!!"); System.out.println("Im in 2!!!\n"); } finally { printLock.unlock(); } try { Thread.sleep(sleepTime); } catch (InterruptedException e) {} } }); t1.start(); t2.start(); t1.join(); t2.join(); } }
Why your mySocketServer approach caused timing issues
Using mySocketServer as a lock is risky because that object’s lock is likely being used by other parts of your application (like network I/O operations). This can lead to unexpected lock contention—your print threads might wait longer than necessary, or other critical operations on the socket server could be blocked, causing weird timing behavior. The solutions above use locks dedicated to the print atomicity requirement, so they don’t interfere with other business logic.
Both solutions work seamlessly with hundreds of threads: ReentrantLock is optimized for high concurrency, and even the System.out lock is sufficient since print operations are I/O-bound (the lock contention won’t be a bottleneck compared to the slow I/O itself).
内容的提问来源于stack exchange,提问作者MrRobot

