synchronized 锁的是对象实例而非代码,非静态方法锁this、静态方法锁类Class对象,显式同步块锁指定对象,不同对象间无竞争。
可以这样跟新手说:synchronized 锁住的不是“哪几行代码”,而是“调用这些代码的那个对象”。关键在于“谁在调用”,而不是“写了什么代码”。
比如你写了一个方法:
public synchronized void doSomething() { ... }
这看起来像“锁住了这个方法”,其实等价于:
public void doSomething() { synchronized(this) { ... } }
也就是说,真正被锁的是当前对象(this),也就是调用这个方法的那个实例。两个线程分别操作两个不同的对象实例,即使调用的是同一个方法,也不会互相阻塞。
立即学习“Java免费学习笔记(深入)”;
假设你有:
线程 A 调用 obj1.doSomething(),线程 B 同时调用 obj2.doSomething() —— 它们互不影响,因为锁的是不同对象。
但若都是调用 obj1.doSomething(),那第二个线程就得等第一个执行完释放锁。
如果写的是:
public static synchronized void doStatic() { ... }
那就等价于:
synchronized(Counter.class) { ... }
这时锁的是 Counter 类对应的 Class 对象,所有该类的实例共享这一把锁。所以静态同步方法和非静态同步方法之间,默认不互斥——它们锁的根本不是同一个东西。
比如:
synchronized(lockA) { ... }<br>synchronized(lockB) { ... }
这里一目了然:前一段锁的是 lockA 这个对象,后一段锁的是 lockB 这个对象。只要 lockA 和 lockB 是两个不同的对象,两段代码就能并发执行。
哪怕它们在同一个方法里、代码完全一样,锁的对象不同,效果就完全不同。