需要对象锁以确保共享计数器的读-改-写操作原子化;不加锁时count++等三步操作可能被线程交错执行导致结果错误;synchronized应作用于共享Counter实例的方法或代码块;Thread.sleep()需放在同步块外模拟干扰,避免持锁阻塞其他线程。

在初级并发练习中,用对象锁(synchronized)配合 Thread.sleep() 实现同步计数,核心是**确保多个线程对共享计数器的读-改-写操作原子化**,同时用 sleep 模拟耗时操作、放大竞态条件,从而直观看到加锁的必要性。
为什么需要对象锁?
不加锁时,多个线程同时执行类似 count++(等价于读取→+1→写回三步),可能互相覆盖中间结果,导致最终计数小于预期。对象锁能保证同一时刻只有一个线程进入临界区,避免交错执行。
怎么用 synchronized 锁住共享资源?
关键不是锁“线程”,而是锁“被多个线程共同访问的对象”:
- 把计数器变量(如
int count)放在一个共享对象里(比如叫Counter类) - 所有修改计数的操作,都写在该对象的
synchronized实例方法中(或用synchronized(this)块) - 多个线程操作的是同一个
Counter实例,才能真正互斥
sleep 的作用和正确位置
Thread.sleep() 在这里是“干扰项”——它让线程暂停,增加其他线程插队的概率,从而更容易暴露没加锁的问题。但它不能放在 synchronized 块内部作为延时逻辑,否则会一直占着锁,失去并发意义:
- ✅ 正确:在
synchronized块外 sleep(模拟线程间随机等待),或仅在读/写之间短暂 sleep(仍需在锁内完成完整更新) - ❌ 错误:在
synchronized方法里长时间 sleep,等于把锁霸占住,其他线程干等
一个可运行的小例子
下面是一个典型教学用法(Java):
class Counter {
private int count = 0;
public synchronized void increment() {
int temp = count; // 读
try { Thread.sleep(1); } catch (InterruptedException e) {}
count = temp + 1; // 写(整个读-改-写被锁保护)
}
public int getCount() { return count; }
}
public class SyncCounterDemo {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("Final count: " + counter.getCount()); // 应输出 2000
}
}
注意:sleep(1) 很短,仅用于增大竞争概率;实际业务中不要在锁内做无谓延迟。真正的同步靠 synchronized 保障,sleep 只是辅助观察。

















